socketft.cpp
// socketft.cpp - written and placed in the public domain by Wei Dai
#include "pch.h"
#include "config.h"
#if !defined(NO_OS_DEPENDENCE) && defined(SOCKETS_AVAILABLE)
// TODO: http://github.com/weidai11/cryptopp/issues/19
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include "socketft.h"
#include "wait.h"
// Windows 8, Windows Server 2012, and Windows Phone 8.1 need <synchapi.h> and <ioapiset.h>
#if defined(CRYPTOPP_WIN32_AVAILABLE)
# if ((WINVER >= 0x0602 /*_WIN32_WINNT_WIN8*/) || (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/))
# include <synchapi.h>
# include <ioapiset.h>
# define USE_WINDOWS8_API
# endif
#endif
#ifdef USE_BERKELEY_STYLE_SOCKETS
#include <errno.h>
#include <netdb.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#endif
#if defined(CRYPTOPP_MSAN)
# include <sanitizer/msan_interface.h>
#endif
#ifdef PREFER_WINDOWS_STYLE_SOCKETS
# pragma comment(lib, "ws2_32.lib")
#endif
NAMESPACE_BEGIN(CryptoPP)
#ifdef USE_WINDOWS_STYLE_SOCKETS
const int SOCKET_EINVAL = WSAEINVAL;
const int SOCKET_EWOULDBLOCK = WSAEWOULDBLOCK;
typedef int socklen_t;
#else
const int SOCKET_EINVAL = EINVAL;
const int SOCKET_EWOULDBLOCK = EWOULDBLOCK;
#endif
// Solaris doesn't have INADDR_NONE
#ifndef INADDR_NONE
# define INADDR_NONE 0xffffffff
#endif /* INADDR_NONE */
Socket::Err::Err(socket_t s, const std::string& operation, int error)
: OS_Error(IO_ERROR, "Socket: " + operation + " operation failed with error " + IntToString(error), operation, error)
, m_s(s)
{
}
Socket::~Socket()
{
if (m_own)
{
try
{
CloseSocket();
}
catch (const Exception&)
{
assert(0);
}
}
}
void Socket::AttachSocket(socket_t s, bool own)
{
if (m_own)
CloseSocket();
m_s = s;
m_own = own;
SocketChanged();
}
socket_t Socket::DetachSocket()
{
socket_t s = m_s;
m_s = INVALID_SOCKET;
SocketChanged();
return s;
}
void Socket::Create(int nType)
{
assert(m_s == INVALID_SOCKET);
m_s = socket(AF_INET, nType, 0);
CheckAndHandleError("socket", m_s);
m_own = true;
SocketChanged();
}
void Socket::CloseSocket()
{
if (m_s != INVALID_SOCKET)
{
#ifdef USE_WINDOWS_STYLE_SOCKETS
# if defined(USE_WINDOWS8_API)
BOOL result = CancelIoEx((HANDLE) m_s, NULL);
assert(result || (!result && GetLastError() == ERROR_NOT_FOUND));
CheckAndHandleError_int("closesocket", closesocket(m_s));
CRYPTOPP_UNUSED(result); // Used by assert in debug builds
# else
BOOL result = CancelIo((HANDLE) m_s);
assert(result || (!result && GetLastError() == ERROR_NOT_FOUND));
CheckAndHandleError_int("closesocket", closesocket(m_s));
CRYPTOPP_UNUSED(result);
# endif
#else
CheckAndHandleError_int("close", close(m_s));
#endif
m_s = INVALID_SOCKET;
SocketChanged();
}
}
void Socket::Bind(unsigned int port, const char *addr)
{
sockaddr_in sa;
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
if (addr == NULL)
sa.sin_addr.s_addr = htonl(INADDR_ANY);
else
{
unsigned long result = inet_addr(addr);
if (result == INADDR_NONE)
{
SetLastError(SOCKET_EINVAL);
CheckAndHandleError_int("inet_addr", SOCKET_ERROR);
}
sa.sin_addr.s_addr = result;
}
sa.sin_port = htons((unsigned short)port);
Bind((sockaddr *)&sa, sizeof(sa));
}
void Socket::Bind(const sockaddr *psa, socklen_t saLen)
{
assert(m_s != INVALID_SOCKET);
// cygwin workaround: needs const_cast
CheckAndHandleError_int("bind", bind(m_s, const_cast<sockaddr *>(psa), saLen));
}
void Socket::Listen(int backlog)
{
assert(m_s != INVALID_SOCKET);
CheckAndHandleError_int("listen", listen(m_s, backlog));
}
bool Socket::Connect(const char *addr, unsigned int port)
{
assert(addr != NULL);
sockaddr_in sa;
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = inet_addr(addr);
if (sa.sin_addr.s_addr == INADDR_NONE)
{
hostent *lphost = gethostbyname(addr);
if (lphost == NULL)
{
SetLastError(SOCKET_EINVAL);
CheckAndHandleError_int("gethostbyname", SOCKET_ERROR);
}
else
{
assert(IsAlignedOn(lphost->h_addr,GetAlignmentOf<in_addr>()));
sa.sin_addr.s_addr = ((in_addr *)(void *)lphost->h_addr)->s_addr;
}
}
sa.sin_port = htons((unsigned short)port);
return Connect((const sockaddr *)&sa, sizeof(sa));
}
bool Socket::Connect(const sockaddr* psa, socklen_t saLen)
{
assert(m_s != INVALID_SOCKET);
int result = connect(m_s, const_cast<sockaddr*>(psa), saLen);
if (result == SOCKET_ERROR && GetLastError() == SOCKET_EWOULDBLOCK)
return false;
CheckAndHandleError_int("connect", result);
return true;
}
bool Socket::Accept(Socket& target, sockaddr *psa, socklen_t *psaLen)
{
assert(m_s != INVALID_SOCKET);
socket_t s = accept(m_s, psa, psaLen);
if (s == INVALID_SOCKET && GetLastError() == SOCKET_EWOULDBLOCK)
return false;
CheckAndHandleError("accept", s);
target.AttachSocket(s, true);
return true;
}
void Socket::GetSockName(sockaddr *psa, socklen_t *psaLen)
{
assert(m_s != INVALID_SOCKET);
CheckAndHandleError_int("getsockname", getsockname(m_s, psa, psaLen));
}
void Socket::GetPeerName(sockaddr *psa, socklen_t *psaLen)
{
assert(m_s != INVALID_SOCKET);
CheckAndHandleError_int("getpeername", getpeername(m_s, psa, psaLen));
}
unsigned int Socket::Send(const byte* buf, size_t bufLen, int flags)
{
assert(m_s != INVALID_SOCKET);
int result = send(m_s, (const char *)buf, UnsignedMin(INT_MAX, bufLen), flags);
CheckAndHandleError_int("send", result);
return result;
}
unsigned int Socket::Receive(byte* buf, size_t bufLen, int flags)
{
assert(m_s != INVALID_SOCKET);
int result = recv(m_s, (char *)buf, UnsignedMin(INT_MAX, bufLen), flags);
CheckAndHandleError_int("recv", result);
return result;
}
void Socket::ShutDown(int how)
{
assert(m_s != INVALID_SOCKET);
int result = shutdown(m_s, how);
CheckAndHandleError_int("shutdown", result);
}
void Socket::IOCtl(long cmd, unsigned long *argp)
{
assert(m_s != INVALID_SOCKET);
#ifdef USE_WINDOWS_STYLE_SOCKETS
CheckAndHandleError_int("ioctlsocket", ioctlsocket(m_s, cmd, argp));
#else
CheckAndHandleError_int("ioctl", ioctl(m_s, cmd, argp));
#endif
}
bool Socket::SendReady(const timeval *timeout)
{
fd_set fds;
FD_ZERO(&fds);
FD_SET(m_s, &fds);
#ifdef CRYPTOPP_MSAN
__msan_unpoison(&fds, sizeof(fds));
#endif
int ready;
if (timeout == NULL)
ready = select((int)m_s+1, NULL, &fds, NULL, NULL);
else
{
timeval timeoutCopy = *timeout; // select() modified timeout on Linux
ready = select((int)m_s+1, NULL, &fds, NULL, &timeoutCopy);
}
CheckAndHandleError_int("select", ready);
return ready > 0;
}
bool Socket::ReceiveReady(const timeval *timeout)
{
fd_set fds;
FD_ZERO(&fds);
FD_SET(m_s, &fds);
#ifdef CRYPTOPP_MSAN
__msan_unpoison(&fds, sizeof(fds));
#endif
int ready;
if (timeout == NULL)
ready = select((int)m_s+1, &fds, NULL, NULL, NULL);
else
{
timeval timeoutCopy = *timeout; // select() modified timeout on Linux
ready = select((int)m_s+1, &fds, NULL, NULL, &timeoutCopy);
}
CheckAndHandleError_int("select", ready);
return ready > 0;
}
unsigned int Socket::PortNameToNumber(const char *name, const char *protocol)
{
int port = atoi(name);
if (IntToString(port) == name)
return port;
servent *se = getservbyname(name, protocol);
if (!se)
throw Err(INVALID_SOCKET, "getservbyname", SOCKET_EINVAL);
return ntohs(se->s_port);
}
void Socket::StartSockets()
{
#ifdef USE_WINDOWS_STYLE_SOCKETS
WSADATA wsd;
int result = WSAStartup(0x0202, &wsd);
if (result != 0)
throw Err(INVALID_SOCKET, "WSAStartup", result);
#endif
}
void Socket::ShutdownSockets()
{
#ifdef USE_WINDOWS_STYLE_SOCKETS
int result = WSACleanup();
if (result != 0)
throw Err(INVALID_SOCKET, "WSACleanup", result);
#endif
}
int Socket::GetLastError()
{
#ifdef USE_WINDOWS_STYLE_SOCKETS
return WSAGetLastError();
#else
return errno;
#endif
}
void Socket::SetLastError(int errorCode)
{
#ifdef USE_WINDOWS_STYLE_SOCKETS
WSASetLastError(errorCode);
#else
errno = errorCode;
#endif
}
void Socket::HandleError(const char *operation) const
{
int err = GetLastError();
throw Err(m_s, operation, err);
}
#ifdef USE_WINDOWS_STYLE_SOCKETS
SocketReceiver::SocketReceiver(Socket &s)
: m_s(s), m_eofReceived(false), m_resultPending(false)
{
m_event.AttachHandle(CreateEvent(NULL, true, false, NULL), true);
m_s.CheckAndHandleError("CreateEvent", m_event.HandleValid());
memset(&m_overlapped, 0, sizeof(m_overlapped));
m_overlapped.hEvent = m_event;
}
SocketReceiver::~SocketReceiver()
{
#ifdef USE_WINDOWS_STYLE_SOCKETS
# if defined(USE_WINDOWS8_API)
BOOL result = CancelIoEx((HANDLE) m_s.GetSocket(), NULL);
assert(result || (!result && GetLastError() == ERROR_NOT_FOUND));
CRYPTOPP_UNUSED(result); // Used by assert in debug builds
# else
BOOL result = CancelIo((HANDLE) m_s.GetSocket());
assert(result || (!result && GetLastError() == ERROR_NOT_FOUND));
CRYPTOPP_UNUSED(result);
# endif
#endif
}
bool SocketReceiver::Receive(byte* buf, size_t bufLen)
{
assert(!m_resultPending && !m_eofReceived);
DWORD flags = 0;
// don't queue too much at once, or we might use up non-paged memory
WSABUF wsabuf = {UnsignedMin((u_long)128*1024, bufLen), (char *)buf};
if (WSARecv(m_s, &wsabuf, 1, &m_lastResult, &flags, &m_overlapped, NULL) == 0)
{
if (m_lastResult == 0)
m_eofReceived = true;
}
else
{
switch (WSAGetLastError())
{
default:
m_s.CheckAndHandleError_int("WSARecv", SOCKET_ERROR);
case WSAEDISCON:
m_lastResult = 0;
m_eofReceived = true;
break;
case WSA_IO_PENDING:
m_resultPending = true;
}
}
return !m_resultPending;
}
void SocketReceiver::GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack)
{
if (m_resultPending)
container.AddHandle(m_event, CallStack("SocketReceiver::GetWaitObjects() - result pending", &callStack));
else if (!m_eofReceived)
container.SetNoWait(CallStack("SocketReceiver::GetWaitObjects() - result ready", &callStack));
}
unsigned int SocketReceiver::GetReceiveResult()
{
if (m_resultPending)
{
DWORD flags = 0;
if (WSAGetOverlappedResult(m_s, &m_overlapped, &m_lastResult, false, &flags))
{
if (m_lastResult == 0)
m_eofReceived = true;
}
else
{
switch (WSAGetLastError())
{
default:
m_s.CheckAndHandleError("WSAGetOverlappedResult", FALSE);
case WSAEDISCON:
m_lastResult = 0;
m_eofReceived = true;
}
}
m_resultPending = false;
}
return m_lastResult;
}
// *************************************************************
SocketSender::SocketSender(Socket &s)
: m_s(s), m_resultPending(false), m_lastResult(0)
{
m_event.AttachHandle(CreateEvent(NULL, true, false, NULL), true);
m_s.CheckAndHandleError("CreateEvent", m_event.HandleValid());
memset(&m_overlapped, 0, sizeof(m_overlapped));
m_overlapped.hEvent = m_event;
}
SocketSender::~SocketSender()
{
#ifdef USE_WINDOWS_STYLE_SOCKETS
# if defined(USE_WINDOWS8_API)
BOOL result = CancelIoEx((HANDLE) m_s.GetSocket(), NULL);
assert(result || (!result && GetLastError() == ERROR_NOT_FOUND));
CRYPTOPP_UNUSED(result); // Used by assert in debug builds
# else
BOOL result = CancelIo((HANDLE) m_s.GetSocket());
assert(result || (!result && GetLastError() == ERROR_NOT_FOUND));
CRYPTOPP_UNUSED(result);
# endif
#endif
}
void SocketSender::Send(const byte* buf, size_t bufLen)
{
assert(!m_resultPending);
DWORD written = 0;
// don't queue too much at once, or we might use up non-paged memory
WSABUF wsabuf = {UnsignedMin((u_long)128*1024, bufLen), (char *)buf};
if (WSASend(m_s, &wsabuf, 1, &written, 0, &m_overlapped, NULL) == 0)
{
m_resultPending = false;
m_lastResult = written;
}
else
{
if (WSAGetLastError() != WSA_IO_PENDING)
m_s.CheckAndHandleError_int("WSASend", SOCKET_ERROR);
m_resultPending = true;
}
}
void SocketSender::SendEof()
{
assert(!m_resultPending);
m_s.ShutDown(SD_SEND);
m_s.CheckAndHandleError("ResetEvent", ResetEvent(m_event));
m_s.CheckAndHandleError_int("WSAEventSelect", WSAEventSelect(m_s, m_event, FD_CLOSE));
m_resultPending = true;
}
bool SocketSender::EofSent()
{
if (m_resultPending)
{
WSANETWORKEVENTS events;
m_s.CheckAndHandleError_int("WSAEnumNetworkEvents", WSAEnumNetworkEvents(m_s, m_event, &events));
if ((events.lNetworkEvents & FD_CLOSE) != FD_CLOSE)
throw Socket::Err(m_s, "WSAEnumNetworkEvents (FD_CLOSE not present)", E_FAIL);
if (events.iErrorCode[FD_CLOSE_BIT] != 0)
throw Socket::Err(m_s, "FD_CLOSE (via WSAEnumNetworkEvents)", events.iErrorCode[FD_CLOSE_BIT]);
m_resultPending = false;
}
return m_lastResult != 0;
}
void SocketSender::GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack)
{
if (m_resultPending)
container.AddHandle(m_event, CallStack("SocketSender::GetWaitObjects() - result pending", &callStack));
else
container.SetNoWait(CallStack("SocketSender::GetWaitObjects() - result ready", &callStack));
}
unsigned int SocketSender::GetSendResult()
{
if (m_resultPending)
{
DWORD flags = 0;
BOOL result = WSAGetOverlappedResult(m_s, &m_overlapped, &m_lastResult, false, &flags);
m_s.CheckAndHandleError("WSAGetOverlappedResult", result);
m_resultPending = false;
}
return m_lastResult;
}
#endif
#ifdef USE_BERKELEY_STYLE_SOCKETS
SocketReceiver::SocketReceiver(Socket &s)
: m_s(s), m_eofReceived(false), m_lastResult(0)
{
}
void SocketReceiver::GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack)
{
if (!m_eofReceived)
container.AddReadFd(m_s, CallStack("SocketReceiver::GetWaitObjects()", &callStack));
}
bool SocketReceiver::Receive(byte* buf, size_t bufLen)
{
m_lastResult = m_s.Receive(buf, bufLen);
if (bufLen > 0 && m_lastResult == 0)
m_eofReceived = true;
return true;
}
unsigned int SocketReceiver::GetReceiveResult()
{
return m_lastResult;
}
SocketSender::SocketSender(Socket &s)
: m_s(s), m_lastResult(0)
{
}
void SocketSender::Send(const byte* buf, size_t bufLen)
{
m_lastResult = m_s.Send(buf, bufLen);
}
void SocketSender::SendEof()
{
m_s.ShutDown(SD_SEND);
}
unsigned int SocketSender::GetSendResult()
{
return m_lastResult;
}
void SocketSender::GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack)
{
container.AddWriteFd(m_s, CallStack("SocketSender::GetWaitObjects()", &callStack));
}
#endif // USE_BERKELEY_STYLE_SOCKETS
NAMESPACE_END
#endif // SOCKETS_AVAILABLE
IsAlignedOn
bool IsAlignedOn(const void *ptr, unsigned int alignment)
Determines whether ptr is aligned to a minimum value.
Definition: misc.h:907
OS_Error
The operating system reported an error.
Definition: cryptlib.h:217
Socket::PortNameToNumber
static unsigned int PortNameToNumber(const char *name, const char *protocol="tcp")
look up the port number given its name, returns 0 if not found
Definition: socketft.cpp:301
UnsignedMin
const T1 UnsignedMin(const T1 &a, const T2 &b)
Safe comparison of values that could be neagtive and incorrectly promoted.
Definition: misc.h:503
Socket::StartSockets
static void StartSockets()
start Windows Sockets 2
Definition: socketft.cpp:313
CallStack
Definition: wait.h:124
SocketReceiver::Receive
bool Receive(byte *buf, size_t bufLen)
receive data from network source, returns whether result is immediately available
Definition: socketft.cpp:382
Socket::Err
exception thrown by Socket class
Definition: socketft.h:48
Exception
Base class for all exceptions thrown by the library.
Definition: cryptlib.h:139
SocketSender::GetWaitObjects
void GetWaitObjects(WaitObjectContainer &container, CallStack const &callStack)
Retrieves waitable objects.
Definition: socketft.cpp:516
Socket::ShutdownSockets
static void ShutdownSockets()
calls WSACleanup for Windows Sockets
Definition: socketft.cpp:323
IntToString
std::string IntToString(T value, unsigned int base=10)
Converts a value to a string.
Definition: misc.h:530
SocketReceiver::GetWaitObjects
void GetWaitObjects(WaitObjectContainer &container, CallStack const &callStack)
Retrieves waitable objects.
Definition: socketft.cpp:411
Socket::GetLastError
static int GetLastError()
returns errno or WSAGetLastError
Definition: socketft.cpp:332
Socket::SetLastError
static void SetLastError(int errorCode)
sets errno or calls WSASetLastError
Definition: socketft.cpp:341
Socket
wrapper for Windows or Berkeley Sockets
Definition: socketft.h:44
CryptoPP
Crypto++ library namespace.
config.h
Library configuration file.
WaitObjectContainer
container of wait objects
Definition: wait.h:169