#include "XmlRpcSocket.h"
#include "XmlRpcUtil.h"
#ifndef MAKEDEPEND
#if defined(_WINDOWS)
# include <stdio.h>
# include <winsock2.h>
//# pragma lib(WS2_32.lib)
# define EINPROGRESS WSAEINPROGRESS
# define EWOULDBLOCK WSAEWOULDBLOCK
# define ETIMEDOUT WSAETIMEDOUT
#else
extern "C" {
# include <unistd.h>
# include <stdio.h>
# include <sys/types.h>
# include <sys/socket.h>
# include <netinet/in.h>
# include <netdb.h>
# include <errno.h>
# include <fcntl.h>
}
#endif // _WINDOWS
#endif // MAKEDEPEND
using namespace XmlRpc;
#if defined(_WINDOWS)
static void initWinSock()
{
static bool wsInit = false;
if (! wsInit)
{
WORD wVersionRequested = MAKEWORD( 2, 0 );
WSADATA wsaData;
WSAStartup(wVersionRequested, &wsaData);
wsInit = true;
}
}
#else
#define initWinSock()
#endif // _WINDOWS
// These errors are not considered fatal for an IO operation; the operation will be re-tried.
static inline bool
nonFatalError()
{
int err = XmlRpcSocket::getError();
return (err == EINPROGRESS || err == EAGAIN || err == EWOULDBLOCK || err == EINTR);
}
int
XmlRpcSocket::socket()
{
initWinSock();
return (int) ::socket(AF_INET, SOCK_STREAM, 0);
}
void
XmlRpcSocket::close(int fd)
{
XmlRpcUtil::log(4, "XmlRpcSocket::close: fd %d.", fd);
#if defined(_WINDOWS)
closesocket(fd);
#else
::close(fd);
#endif // _WINDOWS
}
bool
XmlRpcSocket::setNonBlocking(int fd)
{
#if defined(_WINDOWS)
unsigned long flag = 1;
return (ioctlsocket((SOCKET)fd, FIONBIO, &flag) == 0);
#else
return (fcntl(fd, F_SETFL, O_NONBLOCK) == 0);
#endif // _WINDOWS
}
bool
XmlRpcSocket::setReuseAddr(int fd)
{
// Allow this port to be re-bound immediately so server re-starts are not delayed
int sflag = 1;
return (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&sflag, sizeof(sflag)) == 0);
}
// Bind to a specified port
bool
XmlRpcSocket::bind(int fd, int port)
{
struct sockaddr_in saddr;
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
saddr.sin_port = htons((u_short) port);
return (::bind(fd, (struct sockaddr *)&saddr, sizeof(saddr)) == 0);
}
// Set socket in listen mode
bool
XmlRpcSocket::listen(int fd, int backlog)
{
return (::listen(fd, backlog) == 0);
}
int
XmlRpcSocket::accept(int fd)
{
struct sockaddr_in addr;
#if defined(_WINDOWS)
int
#else
socklen_t
#endif
addrlen = sizeof(addr);
return (int) ::accept(fd, (struct sockaddr*)&addr, &addrlen);
}
// Connect a socket to a server (from a client)
bool
XmlRpcSocket::connect(int fd, std::string& host, int port)
{
struct sockaddr_in saddr;
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
struct hostent *hp = gethostbyname(host.c_str());
if (hp == 0) return false;
saddr.sin_family = hp->h_addrtype;
memcpy(&saddr.sin_addr, hp->h_addr, hp->h_length);
saddr.sin_port = htons((u_short) port);
// For asynch operation, this will return EWOULDBLOCK (windows) or
// EINPROGRESS (linux) and we just need to wait for the socket to be writable...
int result = ::connect(fd, (struct sockaddr *)&saddr, sizeof(saddr));
return result == 0 || nonFatalError();
}
// Read available text from the specified socket. Returns false on error.
bool
XmlRpcSocket::nbRead(int fd, std::string& s, bool *eof)
{
const int READ_SIZE = 4096; // Number of bytes to attempt to read at a time
char readBuf[READ_SIZE];
bool wouldBlock = false;
*eof = false;
while ( ! wouldBlock && ! *eof) {
#if defined(_WINDOWS)
int n = recv(fd, readBuf, READ_SIZE-1, 0);
#else
int n = read(fd, readBuf, READ_SIZE-1);
#endif
XmlRpcUtil::log(5, "XmlRpcSocket::nbRead: read/recv returned %d.", n);
if (n > 0) {
readBuf[n] = 0;
s.append(readBuf, n);
} else if (n == 0) {
*eof = true;
} else if (nonFatalError()) {
wouldBlock = true;
} else {
return false; // Error
}
}
return true;
}
// Write text to the specified socket. Returns false on error.
bool
XmlRpcSocket::nbWrite(int fd, std::string& s, int *bytesSoFar)
{
int nToWrite = int(s.length()) - *bytesSoFar;
char *sp = const_cast<char*>(s.c_str()) + *bytesSoFar;
bool wouldBlock = false;
while ( nToWrite > 0 && ! wouldBlock ) {
#if defined(_WINDOWS)
int n = send(fd, sp, nToWrite, 0);
#else
int n = write(fd, sp, nToWrite);
#endif
XmlRpcUtil::log(5, "XmlRpcSocket::nbWrite: send/write returned %d.", n);
if (n > 0) {
sp += n;
*bytesSoFar += n;
nToWrite -= n;
} else if (nonFatalError()) {
wouldBlock = true;
} else {
return false; // Error
}
}
return true;
}
// Returns last errno
int
XmlRpcSocket::getError()
{
#if defined(_WINDOWS)
return WSAGetLastError();
#else
return errno;
#endif
}
// Returns message corresponding to last errno
std::string
XmlRpcSocket::getErrorMsg()
{
return getErrorMsg(getError());
}
// Returns message corresponding to errno... well, it should anyway
std::string
XmlRpcSocket::getErrorMsg(int error)
{
char err[60];
snprintf(err,sizeof(err),"error %d", error);
return std::string(err);
}