// WMConnection.cpp: implementation of the WMConnection class.
//
//////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2000, 2001 Robert Cunnings. All rights reserved.
//
// You may copy, modify, distribute or publish this code free of charge.
//
// NO WARRANTY: This software is provided AS IS, without warranty of any kind.
//
//////////////////////////////////////////////////////////////////////
//
// WMConnection class - a class for Winsock2 support.
// 
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "WMConnection.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

WMConnection::WMConnection()
{
	m_hSocket = INVALID_SOCKET;
	m_RecvBuffer.buf = m_szRecvBuffer;
	m_RecvBuffer.len = RCV_BUFFER_SIZE;
}

WMConnection::~WMConnection()
{
}

bool WMConnection::Initialize(SOCKET hSocket)
{
	m_hSocket = hSocket;
	return true;
}

void WMConnection::Shutdown()
{
	// shut down socket
	if (m_hSocket != INVALID_SOCKET)
	{
		closesocket(m_hSocket);
	}
	m_hSocket = INVALID_SOCKET;
}

WMConnection::RCVSTATUS  WMConnection::RcvBlock(LPWSABUF pBlock)
{
	// if number bytes is 0 then socket has closed
	if (m_dwNumRecvBytes == 0)
	{
		return RCV_CLOSED;
	}
	// copy data into receive buffer
	pBlock->buf = (char *)malloc(m_dwNumRecvBytes);
	memcpy(pBlock->buf, m_RecvBuffer.buf, m_dwNumRecvBytes);
	pBlock->len = m_dwNumRecvBytes;

	return RCV_SUCCESS;
}

WMConnection::RCVSTATUS  WMConnection::PostAsychRcv()
{
	// post asynch recv
	DWORD dwFlags = 0;
	m_OverlappedRcv.hEvent = NULL;

	int nResult = WSARecv(m_hSocket,
							&m_RecvBuffer,
							1,
							&m_dwNumRecvBytes,
							&dwFlags,
							&m_OverlappedRcv,
							NULL);

	if (nResult == SOCKET_ERROR)
	{
		int nError = WSAGetLastError();
		switch(nError)
		{
			case WSA_IO_PENDING:
				// do nothing
				return RCV_IO_PENDING;

			case WSAEWOULDBLOCK:
				// do nothing
				return RCV_WOULDBLOCK;

			case WSAENETRESET:
			case WSAECONNRESET:
				// connection shut down
				return RCV_RESET;

			default:
				// fatal error
				return RCV_ERROR;
		}
	}
	return RCV_SUCCESS;
}

WMConnection::SENDSTATUS WMConnection::SendBlock(LPWSABUF pBlock)
{
	if (pBlock == NULL)
	{
		return SEND_ERROR;
	}
	// send the data
	DWORD dwNumBytes;
	m_OverlappedSend.hEvent = NULL;

	int nResult = WSASend(m_hSocket,
							pBlock,
							1,
							&dwNumBytes,
							0,
							&m_OverlappedSend,
							NULL);
	if (nResult == SOCKET_ERROR)
	{
		int nError = WSAGetLastError();
		switch(nError)
		{
			case WSA_IO_PENDING:
				// do nothing
				return SEND_IO_PENDING;

			case WSAEWOULDBLOCK:
				// too many outstanding overlapped i/o calls
				return SEND_WOULDBLOCK;

			case WSAENETRESET:
			case WSAECONNRESET:
				// connection reset
				return SEND_RESET;

			default:
				// some other error
				return SEND_ERROR;
		}
	}
	// WSASend returned 0 indicating immediate send
	return SEND_SUCCESS;
}

bool WMConnection::ResolveName(bool bUseDNS, string& strPeerName)
{
	SOCKADDR SockAddr;
	int nSize = sizeof(SOCKADDR);
	if (getpeername(m_hSocket, &SockAddr, &nSize) == SOCKET_ERROR)
	{
		return false;
	}

	struct sockaddr_in *InetAddr = (struct sockaddr_in *)&SockAddr;
	char *pszName = inet_ntoa(InetAddr->sin_addr);
	if (pszName == NULL)
	{
		return false;
	}
	else
	{
		strPeerName = pszName;
		m_strPeerAddress = pszName;
	}
	// if using DNS lookup, try to resolve name
	if (bUseDNS == true)
	{
		if (strPeerName == _T("127.0.0.1"))  // loopback
		{
			strPeerName = _T("loopback");
			m_strPeerName = _T("loopback");
		}
		else
		{
			HOSTENT *pHE;
			if ((pHE = gethostbyaddr((const char *)&InetAddr->sin_addr, 4, PF_INET)) != NULL)
			{
				strPeerName = pHE->h_name;
				m_strPeerName = pHE->h_name;
			}
			else
			{
				return false;
			}
		}
	}
	return true;
}
