#include "XmlRpcSocket.h" #include "XmlRpcUtil.h" #ifndef MAKEDEPEND #if defined(_WINDOWS) # include # include //# pragma lib(WS2_32.lib) # define EINPROGRESS WSAEINPROGRESS # define EWOULDBLOCK WSAEWOULDBLOCK # define ETIMEDOUT WSAETIMEDOUT #else extern "C" { # include # include # include # include # include # include # include # include # include } #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(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); }