XmlRpcSocket.cpp
Go to the documentation of this file.
00001 // this file modified by Morgan Quigley on 22 Apr 2008.
00002 // added features: server can be opened on port 0 and you can read back
00003 // what port the OS gave you
00004 
00005 #include "XmlRpcSocket.h"
00006 #include "XmlRpcUtil.h"
00007 
00008 #ifndef MAKEDEPEND
00009 
00010 #if defined(_WINDOWS)
00011 # include <stdio.h>
00012 # include <winsock2.h>
00013 # include <ws2tcpip.h>
00014 //# pragma lib(WS2_32.lib)
00015 
00016 // MS VS10 actually has these definitions (as opposed to earlier versions),
00017 // so if present, temporarily disable them and reset to WSA codes for this file only.
00018 #ifdef EAGAIN
00019   #undef EAGAIN
00020 #endif
00021 #ifdef EINTR
00022   #undef EINTR
00023 #endif
00024 #ifdef EINPROGRESS
00025   #undef EINPROGRESS
00026 #endif
00027 #ifdef  EWOULDBLOCK
00028   #undef EWOULDBLOCK
00029 #endif
00030 #ifdef ETIMEDOUT
00031   #undef ETIMEDOUT
00032 #endif
00033 # define EAGAIN         WSATRY_AGAIN
00034 # define EINTR                  WSAEINTR
00035 # define EINPROGRESS    WSAEINPROGRESS
00036 # define EWOULDBLOCK    WSAEWOULDBLOCK
00037 # define ETIMEDOUT          WSAETIMEDOUT
00038 #else
00039 extern "C" {
00040 # include <unistd.h>
00041 # include <stdio.h>
00042 # include <sys/types.h>
00043 # include <sys/socket.h>
00044 # include <netinet/in.h>
00045 # include <netdb.h>
00046 # include <errno.h>
00047 # include <fcntl.h>
00048 # include <string.h>
00049 # include <stdlib.h>
00050 # include <arpa/inet.h>
00051 }
00052 #endif  // _WINDOWS
00053 
00054 #endif // MAKEDEPEND
00055 
00056 // MSG_NOSIGNAL does not exists on OS X
00057 #if defined(__APPLE__) || defined(__MACH__)
00058 # ifndef MSG_NOSIGNAL
00059 #   define MSG_NOSIGNAL SO_NOSIGPIPE
00060 # endif
00061 #endif
00062 
00063 
00064 using namespace XmlRpc;
00065 
00066 
00067 bool XmlRpcSocket::s_use_ipv6_ = false;
00068 
00069 #if defined(_WINDOWS)
00070 
00071 static void initWinSock()
00072 {
00073   static bool wsInit = false;
00074   if (! wsInit)
00075   {
00076     WORD wVersionRequested = MAKEWORD( 2, 0 );
00077     WSADATA wsaData;
00078     WSAStartup(wVersionRequested, &wsaData);
00079     wsInit = true;
00080   }
00081 }
00082 
00083 #else
00084 
00085 #define initWinSock()
00086 
00087 #endif // _WINDOWS
00088 
00089 
00090 // These errors are not considered fatal for an IO operation; the operation will be re-tried.
00091 static inline bool
00092 nonFatalError()
00093 {
00094   int err = XmlRpcSocket::getError();
00095   return (err == EINPROGRESS || err == EAGAIN || err == EWOULDBLOCK || err == EINTR);
00096 }
00097 
00098 int
00099 XmlRpcSocket::socket()
00100 {
00101   initWinSock();
00102   return (int) ::socket(s_use_ipv6_ ? AF_INET6 : AF_INET, SOCK_STREAM, 0);
00103 }
00104 
00105 
00106 void
00107 XmlRpcSocket::close(int fd)
00108 {
00109   XmlRpcUtil::log(4, "XmlRpcSocket::close: fd %d.", fd);
00110 #if defined(_WINDOWS)
00111   closesocket(fd);
00112 #else
00113   ::close(fd);
00114 #endif // _WINDOWS
00115 }
00116 
00117 
00118 
00119 
00120 bool
00121 XmlRpcSocket::setNonBlocking(int fd)
00122 {
00123 #if defined(_WINDOWS)
00124   unsigned long flag = 1;
00125   return (ioctlsocket((SOCKET)fd, FIONBIO, &flag) == 0);
00126 #else
00127   return (fcntl(fd, F_SETFL, O_NONBLOCK) == 0);
00128 #endif // _WINDOWS
00129 }
00130 
00131 
00132 bool
00133 XmlRpcSocket::setReuseAddr(int fd)
00134 {
00135   // Allow this port to be re-bound immediately so server re-starts are not delayed
00136   int sflag = 1;
00137   return (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&sflag, sizeof(sflag)) == 0);
00138 }
00139 
00140 
00141 // Bind to a specified port
00142 bool
00143 XmlRpcSocket::bind(int fd, int port)
00144 {
00145   sockaddr_storage ss;
00146   socklen_t ss_len;
00147   memset(&ss, 0, sizeof(ss));
00148 
00149   if (s_use_ipv6_)
00150   {
00151     sockaddr_in6 *address = (sockaddr_in6 *)&ss;
00152     ss_len = sizeof(sockaddr_in6);
00153 
00154     address->sin6_family = AF_INET6;
00155     address->sin6_addr = in6addr_any;
00156     address->sin6_port = htons((u_short) port);
00157   }
00158   else
00159   {
00160     sockaddr_in *address = (sockaddr_in *)&ss;
00161     ss_len = sizeof(sockaddr_in);
00162 
00163     address->sin_family = AF_INET;
00164     address->sin_addr.s_addr = htonl(INADDR_ANY);
00165     address->sin_port = htons((u_short) port);
00166   }
00167 
00168   return (::bind(fd, (sockaddr*)&ss, ss_len) == 0);
00169 }
00170 
00171 
00172 // Set socket in listen mode
00173 bool
00174 XmlRpcSocket::listen(int fd, int backlog)
00175 {
00176   return (::listen(fd, backlog) == 0);
00177 }
00178 
00179 
00180 int
00181 XmlRpcSocket::accept(int fd)
00182 {
00183   struct sockaddr_in addr;
00184 #if defined(_WINDOWS)
00185   int
00186 #else
00187   socklen_t
00188 #endif
00189     addrlen = sizeof(addr);
00190   // accept will truncate the address if the buffer is too small.
00191   // As we are not using it, no special case for IPv6
00192   // has to be made.
00193   return (int) ::accept(fd, (struct sockaddr*)&addr, &addrlen);
00194 }
00195 
00196 
00197 
00198 // Connect a socket to a server (from a client)
00199 bool
00200 XmlRpcSocket::connect(int fd, std::string& host, int port)
00201 {
00202   sockaddr_storage ss;
00203   socklen_t ss_len;
00204   memset(&ss, 0, sizeof(ss));
00205 
00206   struct addrinfo* addr;
00207   struct addrinfo hints;
00208   memset(&hints, 0, sizeof(hints));
00209   hints.ai_family = AF_UNSPEC;
00210   if (getaddrinfo(host.c_str(), NULL, &hints, &addr) != 0)
00211   {
00212     fprintf(stderr, "Couldn't find an %s address for [%s]\n", s_use_ipv6_ ? "AF_INET6" : "AF_INET", host.c_str());
00213     return false;
00214   }
00215 
00216   bool found = false;
00217   struct addrinfo* it = addr;
00218 
00219   socklen_t len;
00220   struct sockaddr *address;
00221 
00222   for (; it; it = it->ai_next)
00223   {
00224     if (!s_use_ipv6_ && it->ai_family == AF_INET)
00225     {
00226       sockaddr_in *address = (sockaddr_in *)&ss;
00227       ss_len = sizeof(sockaddr_in);
00228 
00229       memcpy(address, it->ai_addr, it->ai_addrlen);
00230       address->sin_family = it->ai_family;
00231       address->sin_port = htons((u_short) port);
00232 
00233       XmlRpcUtil::log(5, "found host as %s\n", inet_ntoa(address->sin_addr));
00234       found = true;
00235       break;
00236     }
00237     if (s_use_ipv6_ && it->ai_family == AF_INET6)
00238     {
00239       sockaddr_in6 *address = (sockaddr_in6 *)&ss;
00240       ss_len = sizeof(sockaddr_in6);
00241 
00242       memcpy(address, it->ai_addr, it->ai_addrlen);
00243       address->sin6_family = it->ai_family;
00244       address->sin6_port = htons((u_short) port);
00245 
00246       char buf[128];
00247       // TODO IPV6: check if this also works under Windows
00248       XmlRpcUtil::log(5, "found ipv6 host as %s\n", inet_ntop(AF_INET6, (void*)&(address->sin6_addr), buf, sizeof(buf)));
00249       found = true;
00250       break;
00251     }
00252 
00253   }
00254 
00255   if (!found)
00256   {
00257     printf("Couldn't find an %s address for [%s]\n", s_use_ipv6_ ? "AF_INET6" : "AF_INET", host.c_str());
00258     freeaddrinfo(addr);
00259     return false;
00260   }
00261 
00262   // For asynch operation, this will return EWOULDBLOCK (windows) or
00263   // EINPROGRESS (linux) and we just need to wait for the socket to be writable...
00264   int result = ::connect(fd, (sockaddr*)&ss, ss_len);
00265   if (result != 0 ) {
00266           int error = getError();
00267           if ( (error != EINPROGRESS) && error != EWOULDBLOCK) { // actually, should probably do a platform check here, EWOULDBLOCK on WIN32 and EINPROGRESS otherwise
00268                     printf("::connect error = %d\n", getError());
00269           }
00270   }
00271 
00272   freeaddrinfo(addr);
00273 
00274   return result == 0 || nonFatalError();
00275 }
00276 
00277 
00278 
00279 // Read available text from the specified socket. Returns false on error.
00280 bool
00281 XmlRpcSocket::nbRead(int fd, std::string& s, bool *eof)
00282 {
00283   const int READ_SIZE = 4096;   // Number of bytes to attempt to read at a time
00284   char readBuf[READ_SIZE];
00285 
00286   bool wouldBlock = false;
00287   *eof = false;
00288 
00289   while ( ! wouldBlock && ! *eof) {
00290 #if defined(_WINDOWS)
00291     int n = recv(fd, readBuf, READ_SIZE-1, 0);
00292 #else
00293     int n = read(fd, readBuf, READ_SIZE-1);
00294 #endif
00295     XmlRpcUtil::log(5, "XmlRpcSocket::nbRead: read/recv returned %d.", n);
00296 
00297     if (n > 0) {
00298       readBuf[n] = 0;
00299       s.append(readBuf, n);
00300     } else if (n == 0) {
00301       *eof = true;
00302     } else if (nonFatalError()) {
00303       wouldBlock = true;
00304     } else {
00305       return false;   // Error
00306     }
00307   }
00308   return true;
00309 }
00310 
00311 
00312 // Write text to the specified socket. Returns false on error.
00313 bool
00314 XmlRpcSocket::nbWrite(int fd, std::string& s, int *bytesSoFar)
00315 {
00316   int nToWrite = int(s.length()) - *bytesSoFar;
00317   char *sp = const_cast<char*>(s.c_str()) + *bytesSoFar;
00318   bool wouldBlock = false;
00319 
00320   while ( nToWrite > 0 && ! wouldBlock ) {
00321 #if defined(_WINDOWS)
00322     int n = send(fd, sp, nToWrite, 0);
00323 #else
00324     int n = write(fd, sp, nToWrite);
00325 #endif
00326     XmlRpcUtil::log(5, "XmlRpcSocket::nbWrite: send/write returned %d.", n);
00327 
00328     if (n > 0) {
00329       sp += n;
00330       *bytesSoFar += n;
00331       nToWrite -= n;
00332     } else if (nonFatalError()) {
00333       wouldBlock = true;
00334     } else {
00335       return false;   // Error
00336     }
00337   }
00338   return true;
00339 }
00340 
00341 
00342 // Returns last errno
00343 int
00344 XmlRpcSocket::getError()
00345 {
00346 #if defined(_WINDOWS)
00347   return WSAGetLastError();
00348 #else
00349   return errno;
00350 #endif
00351 }
00352 
00353 
00354 // Returns message corresponding to last errno
00355 std::string
00356 XmlRpcSocket::getErrorMsg()
00357 {
00358   return getErrorMsg(getError());
00359 }
00360 
00361 // Returns message corresponding to errno... well, it should anyway
00362 std::string
00363 XmlRpcSocket::getErrorMsg(int error)
00364 {
00365   char err[60];
00366 #ifdef _MSC_VER
00367   strerror_s(err,60,error);
00368 #else
00369   snprintf(err,sizeof(err),"%s",strerror(error));
00370 #endif
00371   return std::string(err);
00372 }
00373 
00374 int XmlRpcSocket::get_port(int socket)
00375 {
00376   sockaddr_storage ss;
00377   socklen_t ss_len = sizeof(ss);
00378   getsockname(socket, (sockaddr *)&ss, &ss_len);
00379 
00380   sockaddr_in *sin = (sockaddr_in *)&ss;
00381   sockaddr_in6 *sin6 = (sockaddr_in6 *)&ss;
00382   
00383   switch (ss.ss_family)
00384   {
00385     case AF_INET:
00386       return ntohs(sin->sin_port);
00387     case AF_INET6:
00388       return ntohs(sin6->sin6_port);
00389   }  
00390   return 0;
00391 }
00392 


xmlrpcpp
Author(s): Chris Morley, Konstantin Pilipchuk, Morgan Quigley
autogenerated on Fri Aug 28 2015 12:33:06