Click here to Skip to main content
15,885,366 members
Articles / Desktop Programming / MFC

CSPServer, State-based Protocol Server Class

Rate me:
Please Sign up or sign in to vote.
4.88/5 (14 votes)
11 Mar 20038 min read 145K   1.4K   71  
Class framework for creating client/server protocol servers
//***********************************************************************
// (c) Copyright 1999-2003 Santronics Software, Inc. All Rights Reserved.
//***********************************************************************
// File Name : socketio.cpp
// Subsystem : socket I/O wrapper
// Date      : 03/03/2003
// Author    : Hector Santos, Santronics Software, Inc.
// VERSION   : 1.00P
//
// Revision History:
// Version  Date      Author  Comments
// -------  --------  ------  -------------------------------------------
// v1.00P   03/03/03  HLS     Public Release version (non-SSL version)
//***********************************************************************

#include "socketio.h"

#define assert ASSERT
#ifdef _DEBUG
#define new DEBUG_NEW
#endif

///////////////////////////////////////////////////////////////////////

void CSocketIO::InitSocketData()
{
    Socket      = INVALID_SOCKET;  // HLS
    Connected   = FALSE;
}

CSocketIO::CSocketIO()
{
    InitSocketData();
    Config(socket(PF_INET, SOCK_STREAM, 0));
}

CSocketIO::CSocketIO(SOCKET s)
{
    InitSocketData();
    Connected = s != NULL;
    Config(s);
}

CSocketIO::~CSocketIO()
{
   Close();
}

BOOL CSocketIO::Close()
{
    if (!Connected || Socket == INVALID_SOCKET) {
        return FALSE;
    }
    Connected = FALSE;
    closesocket(Socket);
    Socket = INVALID_SOCKET;
    return TRUE;
}


void CSocketIO::Config(SOCKET s)
{
    if (s == NULL) {
        TRACE("Socket is NULL");
    }

    Socket        = s;
    BufferIndex   = 0;
    BufferTop     = 0;
    LastStatus    = 0;
}


BOOL CSocketIO::Connect(sockaddr_in src,
                        sockaddr_in dest,
                        const BOOL reservedport)
{
    if (Connected) return FALSE;

    BOOL tru = TRUE;
    ::setsockopt(Socket, SOL_SOCKET, SO_REUSEADDR, (const char *)&tru, sizeof(tru));

    if (reservedport) {
        while (1) {
            int lport = IPPORT_RESERVED-1;
            if (lport <= IPPORT_RESERVED/2) {
                DWORD err = GetLastError();
                closesocket(Socket);
                SetLastError(err);
                Socket = INVALID_SOCKET;
                return FALSE;
            }
            sin.sin_port = htons(lport);
            if (bind(Socket, (sockaddr *)&src, sizeof(src)) == 0) {
                break;
            }
            lport--;
        }
    } else if (bind(Socket, (sockaddr *)&src, sizeof(src)) != 0) {
        DWORD err = GetLastError();
        closesocket(Socket);
        SetLastError(err);
        Socket = INVALID_SOCKET;
        return FALSE;
    }

    if (connect(Socket, (sockaddr *)&dest, sizeof(dest)) != 0) {
        DWORD err = GetLastError();
        closesocket(Socket);
        SetLastError(err);
        Socket = INVALID_SOCKET;
        return FALSE;
    }
    
    Connected = TRUE;
    BufferIndex = 0;
    BufferTop = 0;
    return TRUE;
}

BOOL CSocketIO::Connect(const TCHAR *address,
                        int port,
                        const BOOL reservedport)
{
    if (Connected) return FALSE;

    sockaddr_in dest;
    dest.sin_family = AF_INET;
    dest.sin_addr.s_addr = inet_addr(address);
    if (dest.sin_addr.s_addr==INADDR_NONE) return FALSE;
    dest.sin_port = htons(port);

    sin.sin_family = AF_INET;
    sin.sin_port = 0;
    sin.sin_addr.s_addr = INADDR_ANY;

    return Connect(sin, dest, reservedport);
}

BOOL CSocketIO::Open(const TCHAR *address,
                     int port,
                     const BOOL reservedport)
{
    if (Connected) return FALSE;
#ifdef _UNICODE
    char adr[MAX_PATH];
    WideCharToMultiByte(CP_ACP, 0, address, -1, adr, sizeof(adr), NULL, NULL);
#else
    const char *adr = address;
#endif
    DWORD addr = inet_addr(adr);
    if (addr == INADDR_NONE) {
        hostent *he = gethostbyname(adr);
        if (!he) {
            return FALSE;
        }
        addr = *(DWORD *)he->h_addr;
        if (!addr) {
            addr = INADDR_NONE;
        }
    }
    if (addr == INADDR_NONE) {
        return FALSE;
    }

    sockaddr_in dest;
    dest.sin_family = AF_INET;
    dest.sin_addr.s_addr = addr;
    dest.sin_port = htons(port);

    sin.sin_family = AF_INET;
    sin.sin_port = 0;
    sin.sin_addr.s_addr = INADDR_ANY;

    return Connect(sin, dest, reservedport);
}


BOOL CSocketIO::Connect(const TCHAR *faddress,
                        int fport,
                        const TCHAR *address,
                        int port,
                        const BOOL reservedport)
{
    if (Connected) return FALSE;

    sockaddr_in dest;
    dest.sin_family = AF_INET;
    dest.sin_addr.s_addr = inet_addr(address);
    if (dest.sin_addr.s_addr==INADDR_NONE) return FALSE;
    dest.sin_port = htons(port);

    sin.sin_family = AF_INET;
    sin.sin_port = htons(fport);
    if (faddress == NULL) {
        sin.sin_addr.s_addr = INADDR_ANY;
    } else {
        sin.sin_addr.s_addr = inet_addr(faddress);
        if (sin.sin_addr.s_addr==INADDR_NONE) return FALSE;
    }

    return Connect(sin, dest, reservedport);
}

int CSocketIO::getpeername(sockaddr *sAddr)
{
    int x = sizeof(sockaddr);
    // a GPF can occur here. why?
    if (Socket == INVALID_SOCKET) {
        return 0;
    }
    if (sAddr == NULL){
        return 0;
    }
    int i = ::getpeername(Socket, sAddr, &x);
    return i;
}

int CSocketIO::getsockname(sockaddr *sAddr)
{
    int x = sizeof(sockaddr);
    return ::getsockname(Socket, sAddr, &x);
}

int CSocketIO::GetSocketPort()
{
    sockaddr_in sin;
    int x = sizeof(sin);
    if (::getsockname(Socket, (sockaddr *)&sin, &x) == SOCKET_ERROR) {
		return 0;
	}
    return ntohs(sin.sin_port);
}

BOOL CSocketIO::Recv(BYTE &c)
{
    if (!Connected) return FALSE;

    if (Socket == INVALID_SOCKET) {
        return FALSE;
    }
    if (BufferIndex >= BufferTop) {
        if (!FillBuffer(0)) {
            return FALSE;
        }
    }
    c = Buffer[BufferIndex++];
    return TRUE;
}

int CSocketIO::Recv(BYTE *buf, int bufsize, DWORD timeoutms)
{
   if (!Connected) return 0;

   int r = 0;
   if (BufferIndex < BufferTop) {
     if (bufsize <= BufferTop-BufferIndex) {
       CopyMemory(buf, &Buffer[BufferIndex], bufsize);
       BufferIndex += bufsize;
       return bufsize;
     }
     int n = BufferTop-BufferIndex;
     CopyMemory(buf, &Buffer[BufferIndex], n);
     buf += n;
     bufsize -= n;
     r += n;
     BufferIndex = BufferTop;
   }
   // Only block if there is no data available to return to the caller
   while (FillBuffer(r == 0 ? timeoutms : 0)) {
     if (bufsize <= BufferTop) {
       CopyMemory(buf, Buffer, bufsize);
       BufferIndex += bufsize;
       r += bufsize;
       return r;
     }
     CopyMemory(buf, Buffer, BufferTop);
     BufferIndex = BufferTop;
     buf += BufferTop;
     bufsize -= BufferTop;
     r += BufferTop;
   }
   return r;
}

DWORD CSocketIO::Send(const char *buf, DWORD bufsize, DWORD flags)
{
    if (!Connected) return FALSE;
    
    if (Socket == INVALID_SOCKET) {
        return -1;
    }
    
    int n = send(buf, bufsize, flags);
    // send will set n to SOCKET_ERROR if there is a problem.
    if (n == SOCKET_ERROR) {
        Close();
        return n;
    }
    return n;
}

DWORD CSocketIO::Send(const wchar_t *s)
{
    if (!Connected) return -1;

    int len = wcslen(s);
    char *t = (char *)malloc(len+1);
    if (t == NULL) {
        return 0;
    }
    WideCharToMultiByte(CP_ACP, 0, s, -1, t, len+1, NULL, NULL);
    DWORD r = Send(t, len+1);
    free(t);
    return r;
}

DWORD CSocketIO::WaitForReceivedData(DWORD timeoutms, HANDLE terminate)
{
    if (!Connected) return 0;

    if (Socket == INVALID_SOCKET) {
        return WAIT_ABANDONED;
    }

    if (BufferIndex < BufferTop) {
        return WAIT_OBJECT_0;
    }

    DWORD start = GetTickCount();
    do {
        fd_set fds;
        FD_ZERO(&fds);
        FD_SET(Socket, &fds);
        timeval tv;
        DWORD elapsed = GetTickCount() - start;
        if (elapsed >= timeoutms) {
            tv.tv_sec = 0;
            tv.tv_usec = 0;
        } else {
            DWORD left = timeoutms - (GetTickCount() - start);
            if (left >= 1000) {
                tv.tv_sec = 1;
                tv.tv_usec = 0;
            } else {
                tv.tv_sec = 0;
                tv.tv_usec = left * 1000;
            }
        }

        int ret = select(0, &fds, NULL, NULL, &tv);
        if (ret == 1) {
            //TRACE("FilleBuffer() select() ret: %d err: %d\n",ret,WSAGetLastError());
            return WAIT_OBJECT_0;
        }
    } while (((timeoutms == INFINITE) || ((GetTickCount() - start) < timeoutms))
        && (Socket != INVALID_SOCKET) && !CheckAbort()
        && ((terminate == NULL) || (WaitForSingleObject(terminate, 0) != WAIT_OBJECT_0)));

    return WAIT_TIMEOUT;
}

BOOL CSocketIO::RecvStr(char *buf, size_t maxlen, int timeout /* = 60 */)
{
    if (!Connected) return FALSE;

    DWORD Status = 0;
    LastStatus = Status;

    memset(buf, 0, maxlen);
    while (TRUE) {
        if (!Online()) {
            return FALSE;
        }
        Status = WaitForReceivedData(timeout == INFINITE ? INFINITE : timeout * 1000);
        LastStatus = Status;
        if (Status == WAIT_ABANDONED || Status == WAIT_TIMEOUT) {
            return FALSE;
        }
        unsigned char c = 0;
        if (!Recv(c)) {
            return FALSE;
        }
        if (c == 10) {
            break;
        }
        buf[strlen(buf)] = c;
        if (strlen(buf) >= maxlen-1) {
            break; // Got more than enough...
        }
    }

    if (buf[strlen(buf)-1] == 13) {
        buf[strlen(buf)-1] = 0;
    }
    return TRUE;
}

BOOL CSocketIO::RecvStr(wchar_t *buf, size_t maxlen, int timeout  /* = 60 */)
{
   if (!Connected) return FALSE;

   char *t = (char *)malloc(maxlen);
   if (t == NULL) {
       return FALSE;
   }
   BOOL r = RecvStr(t, maxlen, timeout);
   if (r) {
     MultiByteToWideChar(CP_ACP, 0, t, -1, buf, maxlen);
   }
   free(t);
   return r;
}

// 450.3b6 - changed return to DWORD from void
DWORD CSocketIO::Sendf(const TCHAR *format, ...)
{
   if (!Connected) return -1;

   va_list args;
   va_start(args, format);
   TCHAR buf[MAX_OUTPUT];
   _vsntprintf(buf, sizeof(buf), format, args);
   return Send(buf, strlen(buf));
}

BOOL CSocketIO::FillBuffer(DWORD timeoutms)
{
    if (!Connected) return FALSE;

    fd_set fds;
    FD_ZERO(&fds);
    FD_SET(Socket, &fds);
    timeval tv;
    tv.tv_sec = timeoutms / 1000;
    tv.tv_usec = (timeoutms % 1000) * 1000;
    if (select(0, &fds, NULL, NULL, &tv) != 1) {
        return FALSE;
    }
    int n = recv((char *)Buffer, sizeof(Buffer),0);
    if (n > 0) {
        BufferTop = n;
        BufferIndex = 0;
        return TRUE;
    }
    int err = WSAGetLastError();       // 450.3b7
    Close();
    if (n == 0) err = WSAECONNABORTED; // 450.3b7
    WSASetLastError(err);              // 450.3b7
    return FALSE;
}

int CSocketIO::recv(char *buf, int bufsize, DWORD flags)
{
   return ::recv(Socket, (char *)buf, bufsize, flags);
}

int CSocketIO::send(const char *buf, int len, int flags)
{
   return ::send(Socket, (char *)buf, len, flags);
}

DWORD CSocketIO::Peek()
{
   if (!Connected) return 0;

   // this is a synchronous wait
   DWORD size = 0;
   ioctlsocket(Socket, FIONREAD, &size);
   return size;
}


BOOL CSocketIO::Ourselves()
{
   sockaddr_in other;
   if (getpeername((sockaddr *)&other) == 0) {
     sockaddr_in us;
     int u = sizeof(us);
     if (getsockname((sockaddr *)&us) == 0) {
       if (other.sin_addr.s_addr == us.sin_addr.s_addr) {
         return TRUE;
       }
     }
   }
   return FALSE;
}


/// same as fillbuffer, can we combine the two?
/// getblock only used in smtpserv.cpp
int CSocketIO::GetBlock(char *data, int len, const char *state, int timeout, int milliseconds)
{
   if (!Connected) return 0;

   /////////////////////////////////////////////////////////
   // 450.3b11 - only do select if a time is specified
   //          - FORCED TO BACK TO ORIGINAL LOGIC BECAUSE SOME CODE MADE CALL GETBLOCK
   //            TO FLUSH THE SOCKET
   if (TRUE || timeout || milliseconds) {
       fd_set fds;
       FD_ZERO(&fds);
       FD_SET(Socket, &fds);
       timeval tv;
       tv.tv_sec = timeout;
       tv.tv_usec = milliseconds;

       // possible return values
       // n > 0     number of sockets fitting criteria
       // n == -1   SOCKET_ERROR
       // n == 0    timelimit expired

       int n = select(0, &fds, NULL, NULL, &tv);

       if (n == 0) {
           return 0;
       }

       if (n == SOCKET_ERROR) {
           int err = GetLastError(); // 450.3b11
           if (err == WSAECONNABORTED) {
               int type;
               int n = sizeof(type);
               int sockerr = getsockopt(SOL_SOCKET, SO_TYPE, (char *)&type, &n);
               if (sockerr == SOCKET_ERROR) {
                   Socket = 0;
                   SetLastError(WSAECONNABORTED);
               }
           }
           return 0;
       }
   }
   /////////////////////////////////////////////////////////

   int rlen=recv(data, len, 0);
   if (rlen <= 0) {
     Close();
   }
   return rlen;
}


BOOL CSocketIO::Listen(const char *address, WORD port, WORD Backlog)
{
	if (Socket == INVALID_SOCKET) {
		return FALSE;
	}
	if (Connected) return FALSE;

	BOOL tru = TRUE;
	::setsockopt(Socket, SOL_SOCKET, SO_REUSEADDR, (const char *)&tru, sizeof(tru));
	sin.sin_family = AF_INET;
	// 450.3b7 note
	//  - if port = 0, winsock returns port between 1024 and 5000 (see SDK bind())
	sin.sin_port = htons(port);
	if (address == NULL) {
		sin.sin_addr.s_addr = INADDR_ANY;
	} else {
		sin.sin_addr.s_addr = inet_addr(address);
		if (sin.sin_addr.s_addr==INADDR_NONE) return FALSE;
	}

	if (bind(Socket, (sockaddr *)&sin, sizeof(sin)) != 0) {
		return FALSE;
	}
	if (listen(Socket, Backlog) != 0) {
		Socket=0;
		return FALSE;
	}
	return TRUE;
}



/* virtual */
BOOL CSocketIO::CheckIPSocketFilter(SOCKET SocketIn)
{
    return TRUE; // accept connection
}

BOOL CSocketIO::Accept()
{
    if (Connected) return FALSE;
    if (Socket == INVALID_SOCKET) {
      return FALSE;
    }

    sockaddr_in src;
    int x = sizeof(src);

    SOCKET SocketIn = accept(Socket, (sockaddr *)&src, &x);

    if (SocketIn == FALSE) {
        //TRACELOG(LOGERROR,"Error %d from accept()", GetLastError());
        return FALSE;
    }

    if (!CheckIPSocketFilter(SocketIn)) {
        //TRACELOG(LOGERROR,"ip filtered. connection rejected");
        closesocket(SocketIn);
        return FALSE;
    }

    closesocket(Socket);
    Socket = SocketIn;
    Connected = TRUE;

    return TRUE;
}

int CSocketIO::setsockopt(int level, int optname, const char *optval, int optlen)
{
    return ::setsockopt(Socket, level, optname, optval, optlen);
}

int CSocketIO::getsockopt(int level, int optname, char *optval, int *optlen)
{
    return ::getsockopt(Socket, level, optname, optval, optlen);
}

// 450.3
int CSocketIO::Select(const int secs /* = 0 */, const int usecs /* = 0 */)
{
    if (Socket == INVALID_SOCKET) return FALSE;
	fd_set fds;
    FD_ZERO(&fds);
    FD_SET(Socket, &fds);
    struct timeval tv;
    tv.tv_sec = secs;
    tv.tv_usec = usecs;
    return select(0, &fds, NULL, NULL, &tv);
}

BOOL CSocketIO::IsConnected()
{
    int ret = Select(0,0);
    if (ret == SOCKET_ERROR) {
       return FALSE;
    }
    return TRUE;
}

// 450.3b7
CString CSocketIO::GetPeerDomainName(const BOOL rdns /*= TRUE*/)
{
	sockaddr_in peer;
	if (getpeername((sockaddr *)&peer) == 0) {
		if (peer.sin_addr.s_addr == 0x0100007f) {
			return "localhost";
		}
		if (rdns) {
		   hostent *host = gethostbyaddr((char *)&peer.sin_addr, 4, PF_INET);
   		   if (host) return host->h_name;
		}
	}
    return "unknown";
}

// 450.3b8
void CSocketIO::GetPeerInfo(CString &sDomain, CString &sIp, const BOOL rdns /*= TRUE*/)
{
    sDomain = "unknown";
    sIp     = "0.0.0.0";
	sockaddr_in peer;
	if (getpeername((sockaddr *)&peer) == 0) {
		if (peer.sin_addr.s_addr == 0x0100007f) {
			sDomain ="localhost";
		}
		if (rdns) {
		   hostent *host = gethostbyaddr((char *)&peer.sin_addr, 4, PF_INET);
   		   if (host) sDomain = host->h_name;
		}
	}
    sIp.Format("%s",inet_ntoa(peer.sin_addr));
    return;
}


// 450.3b8
CString CSocketIO::GetHostDomainName(const BOOL rdns /*= TRUE*/)
{
	sockaddr_in src;
	if (getsockname((sockaddr *)&src) == 0) {
		if (src.sin_addr.s_addr == 0x0100007f) {
			return "localhost";
		}
		if (rdns) {
		   hostent *host = gethostbyaddr((char *)&src.sin_addr, 4, PF_INET);
   		   if (host) return host->h_name;
		}
	}
    return "unknown";
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions