//***********************************************************************
// (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";
}