#ifdef _WIN32 #undef _WIN32_WINNT #define _WIN32_WINNT 0x0601 #include "socketOps.h" #include #include #include #include "stl/stringUtils.h" #include "services/stdServiceImpl.h" using namespace std; using namespace stringUtil; static std::map s_errMsgs; const SOCKET socketOps::cINVALID_SOCKET = INVALID_SOCKET; const SOCKET socketOps::cSOCKET_ERROR = (SOCKET)SOCKET_ERROR; class win32_socket_init { public: ~win32_socket_init() { ::WSACleanup(); } win32_socket_init() { WORD wVersion = MAKEWORD( 1, 1 ); WSADATA wsaData = {0}; ::WSAStartup(wVersion, &wsaData); s_errMsgs[WSAEINTR] = "Interrupted function call: A blocking operation was interrupted by a call to WSACancelBlockingCall."; s_errMsgs[WSAEFAULT] = "Bad address: The system detected an invalid pointer address in attempting to use a pointer argument of a call."; s_errMsgs[WSAEINVAL] = "Invalid argument: Some invalid argument was supplied."; s_errMsgs[WSAEMFILE] = "Too many open descriptors: No more socket descriptors are available."; s_errMsgs[WSAEWOULDBLOCK] = "Call would block: Non-blocking call will block."; s_errMsgs[WSAEINPROGRESS] = "Operation now in progress: A blocking operation is currently executing."; s_errMsgs[WSAEALREADY] = "Operation already in progress: An operation was attempted on a nonblocking socket with an operation already in progress."; s_errMsgs[WSAENOTSOCK] = "Socket operation on non socket: An operation was attempted on something that is not a socket."; s_errMsgs[WSAEDESTADDRREQ] = "Destination address required: A required address was omitted from an operation on a socket."; s_errMsgs[WSAEMSGSIZE] = "Message too long: A message sent on a datagram socket was larger than the internal message buffer."; s_errMsgs[WSAEPROTOTYPE] = "The specified protocol is the wrong type for this socket."; s_errMsgs[WSAENOPROTOOPT] = "Bad Protocol option."; s_errMsgs[WSAEPROTONOSUPPORT] = "The specified protocol is not supported."; s_errMsgs[WSAESOCKTNOSUPPORT] = "The specified socket type is not supported in this address family."; s_errMsgs[WSAEOPNOTSUPP] = "Socket operation not supported."; s_errMsgs[WSAEPFNOSUPPORT] = "Protocol family not supported."; s_errMsgs[WSAEAFNOSUPPORT] = "The specified address family is not supported"; s_errMsgs[WSAEADDRINUSE] = "Address already in use."; s_errMsgs[WSAEADDRNOTAVAIL] = "Cannot assign requested address."; s_errMsgs[WSAENETDOWN] = "A network subsystem or the associated service provider has failed"; s_errMsgs[WSAENETUNREACH] = "Nework is unreachable."; s_errMsgs[WSAENETRESET] = "Network dropped connection on reset."; s_errMsgs[WSAECONNABORTED] = "Software caused connection abort."; s_errMsgs[WSAECONNRESET] = "Connection reset by peer."; s_errMsgs[WSAENOBUFS] = "No buffer space is available. The socket cannot be created."; s_errMsgs[WSAEISCONN] = "Socket is already connected."; s_errMsgs[WSAENOTCONN] = "Socket is not connected."; s_errMsgs[WSAESHUTDOWN] = "Cannot send after socket shutdown."; s_errMsgs[WSAETIMEDOUT] = "Connection timed out."; s_errMsgs[WSAECONNREFUSED] = "Connection refused."; s_errMsgs[WSAEHOSTDOWN] = "Host is down."; s_errMsgs[WSAEHOSTUNREACH] = "No route to host."; s_errMsgs[WSAEPROCLIM] = "Too many processes."; } }; static win32_socket_init win32_socket_init_force; std::string socketOps::errMsg(int errCode) throw() { std::map::const_iterator i = s_errMsgs.find(errCode); return (i == s_errMsgs.end() ? "error code " + tos(errCode) : (*i).second); } int socketOps::errCode() throw() { return ::WSAGetLastError(); } #else #include #include "socketOps.h" #include "stl/stringUtils.h" #include using namespace std; using namespace stringUtil; const int socketOps::cINVALID_SOCKET = -1; const int socketOps::cSOCKET_ERROR = -1; int socketOps::errCode() throw() { return errno; } std::string socketOps::errMsg(int errCode) throw() { std::string result = "error code " + tos(errCode); char *e = strerror(errCode); if (e) { result = e; } return result; } #endif std::string socketOps::endpoint::toString() const throw() { return m_address + ":" + tos(m_port); } socketOps::tSOCKET socketOps::createTCPSocket() throw() { return ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); } socketOps::tSOCKET socketOps::createTCPSocketTHROW() throw(std::runtime_error) { tSOCKET result = socketOps::createTCPSocket(); if (result == socketOps::cINVALID_SOCKET) { throw std::runtime_error("socketOps::createTCPSocketTHROW() - Could not create socket because " + socketOps::errMsg()); } return result; } int socketOps::setNonblock(const socketOps::tSOCKET s, bool nonBlock) throw() { #ifdef _WIN32 unsigned long i = (nonBlock ? 1 : 0); return ioctlsocket(s, FIONBIO, &i); #else int flags, err; if ((flags = fcntl(s, F_GETFL, 0)) == socketOps::cSOCKET_ERROR) { return flags; } if (nonBlock) { flags |= O_NONBLOCK; } else { flags &= ~O_NONBLOCK; } if ((err = fcntl(s, F_SETFL, flags)) == socketOps::cSOCKET_ERROR) { return err; } return 0; #endif } void socketOps::setNonblockTHROW(const socketOps::tSOCKET s, const bool nonblock) throw(std::runtime_error) { int err = socketOps::setNonblock(s, nonblock); if (err == socketOps::cSOCKET_ERROR) { throw std::runtime_error("socketOps::setNonblockTHROW() - Could not get socket flags because " + socketOps::errMsg()); } } void socketOps::closeTCPSocket(socketOps::tSOCKET s) throw() { #ifdef _WIN32 ::shutdown(s, SD_BOTH); ::closesocket(s); #else ::shutdown(s, SHUT_RDWR); ::close(s); #endif } void socketOps::forgetTCPSocket(tSOCKET &s) throw() { if (s != socketOps::cINVALID_SOCKET) { socketOps::closeTCPSocket(s); s = socketOps::cINVALID_SOCKET; } } int socketOps::connect(const socketOps::tSOCKET s, const std::string &address, const u_short port) throw() { unsigned long iaddr = inet_addr(address.c_str()); struct sockaddr_in addr = {0}; addr.sin_family = AF_INET; addr.sin_port = htons(port); memcpy(&(addr.sin_addr), &iaddr, 4); return ::connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr)); } int socketOps::connect(tSOCKET s,const endpoint &e) throw() { return socketOps::connect(s, e.m_address, e.m_port); } void socketOps::connectTHROW(const socketOps::tSOCKET s, const std::string &address, const u_short port) throw(std::runtime_error) { if (socketOps::connect(s, address, port) == socketOps::cSOCKET_ERROR) { int err = socketOps::errCode(); #ifdef _WIN32 if ((err == WSAEINPROGRESS) || (err == WSAEWOULDBLOCK)) #else if ((err == EINPROGRESS) || (err == EWOULDBLOCK) || (err == ECONNABORTED) || (err == EINTR)) #endif return; throw std::runtime_error("Could not connect to " + address + ":" + tos(port) + " because " + socketOps::errMsg()); } } void socketOps::connectTHROW(tSOCKET s, const endpoint &e) throw(std::runtime_error) { if (socketOps::connect(s, e) == socketOps::cSOCKET_ERROR) { int err = socketOps::errCode(); #ifdef _WIN32 if ((err == WSAEINPROGRESS) || (err == WSAEWOULDBLOCK)) #else if ((err == EINPROGRESS) || (err == EWOULDBLOCK) || (err == ECONNABORTED) || (err == EINTR)) #endif return; throw std::runtime_error("Could not connect to " + e.toString() + " because " + socketOps::errMsg()); } } socketOps::nonBlockConnect_t socketOps::nonBlockingConnectWait(const socketOps::tSOCKET s, std::string &errorString) throw() { try { #ifdef _WIN32 // non-blocking connects suck fd_set rset, wset, eset; struct timeval tval; FD_ZERO(&rset); FD_ZERO(&wset); FD_ZERO(&eset); FD_SET(s,&rset); FD_SET(s,&wset); FD_SET(s,&eset); tval.tv_sec = 0; tval.tv_usec = 1; int n = ::select(((int)s)+1,&rset,&wset,&eset,&tval); // if n == 0 then we timed out, and must stay in this state if (n == 0) { return socketOps::NBC_INPROGRESS; } if (n < 0) // fatal select error { try { errorString = socketOps::errMsg(); } catch(...) { errorString = "Impossible connection failure. (1)"; } return socketOps::NBC_ERROR; } // detecting when a non-blocking connect is ready (or has errored) // is highly platform dependent. Microsoft docs specify one way, Stevens // (2nd edition page 411) specifies another way to cover various Unix flavors if (FD_ISSET(s,&eset)) { errorString = "Connection failure."; return socketOps::NBC_ERROR; } else if (FD_ISSET(s,&wset)) { return socketOps::NBC_CONNECTED; } else { errorString = "Impossible connection failure. (2)"; return socketOps::NBC_ERROR; } #else struct pollfd check; int val = -1; socklen_t size = sizeof val; check.fd = s; check.events = POLLOUT; switch (poll (&check, 1, 0)) { case 0: { return socketOps::NBC_INPROGRESS; } default: { if (getsockopt (s, SOL_SOCKET, SO_ERROR, (void*) &val, &size) == 0) { if (val == 0) { return socketOps::NBC_CONNECTED; } errno = val; } // fall thru } case -1: { if (errno == EINTR) { return socketOps::NBC_INPROGRESS; } throw std::runtime_error(""); } } #endif } catch(...) { #ifdef _WIN32 errorString = "Impossible connection failure. (3)"; #else errorString = "Connection failure."; #endif return socketOps::NBC_ERROR; } } socketOps::tSOCKET socketOps::bind(tSOCKET s, u_short port, const std::string &address) throw() { struct sockaddr_in servaddr = {0}; servaddr.sin_family = AF_INET; if (address == "") { servaddr.sin_addr.s_addr = htonl(INADDR_ANY); } else { unsigned long iaddr = inet_addr(address.c_str()); memcpy(&(servaddr.sin_addr), &iaddr, 4); } servaddr.sin_port = htons(port); return ::bind(s, (const sockaddr *)&servaddr, sizeof(servaddr)); } socketOps::tSOCKET socketOps::bind(tSOCKET s, const socketOps::endpoint &e) throw() { return socketOps::bind(s,e.m_port,e.m_address); } void socketOps::bindTHROW(tSOCKET s, u_short port, const std::string &address) throw(std::runtime_error) { if (socketOps::bind(s,port,address) == socketOps::cSOCKET_ERROR) { throw std::runtime_error("Could not bind to " + (!address.empty() ? address + ":" : "") + tos(port) + " because " + socketOps::errMsg()); } } void socketOps::bindTHROW(tSOCKET s,const endpoint &e) throw(std::runtime_error) { if (socketOps::bind(s,e) == socketOps::cSOCKET_ERROR) { throw std::runtime_error("Could not bind to " + e.toString() + " because " + socketOps::errMsg()); } } int socketOps::accept(tSOCKET s, std::string &address, u_short &port) throw() { struct sockaddr_in client_addr = {0}; #ifdef _WIN32 int len = sizeof(client_addr); #else socklen_t len = sizeof(client_addr); #endif socketOps::tSOCKET sC = ::accept(s, (sockaddr*)&client_addr, &len); if (sC != socketOps::cSOCKET_ERROR) { port = ntohs(client_addr.sin_port); address = inet_ntoa(client_addr.sin_addr); } return (int)sC; } int socketOps::accept(tSOCKET s,endpoint &e) throw() { return socketOps::accept(s,e.m_address,e.m_port); } int socketOps::acceptTHROW(tSOCKET s, std::string &address, u_short &port, bool nonblocking) throw(std::runtime_error) { socketOps::tSOCKET sC = socketOps::accept(s, address, port); if (sC == socketOps::cSOCKET_ERROR) { if (!nonblocking) { throw std::runtime_error(socketOps::errMsg()); } int e = socketOps::errCode(); #ifdef _WIN32 if ((e == WSAEINPROGRESS) || (e == WSAEWOULDBLOCK)) { return (int)socketOps::cSOCKET_ERROR; } #else if ((e == EWOULDBLOCK) || (e == ECONNABORTED) || (e == EINTR)) { return socketOps::cSOCKET_ERROR; } #endif throw std::runtime_error("Could not call accept() on socket because " + socketOps::errMsg(e)); } return (int)sC; } int socketOps::acceptTHROW(tSOCKET s, endpoint &e, bool nonblocking) throw(std::runtime_error) { return socketOps::acceptTHROW(s, e.m_address, e.m_port, nonblocking); } void socketOps::listenTHROW(tSOCKET s, int backlog) throw(std::runtime_error) { if (::listen(s, backlog) == socketOps::cSOCKET_ERROR) { throw std::runtime_error("Could not call listen() on socket because " + socketOps::errMsg()); } } int socketOps::getsockname(tSOCKET s, std::string &address, u_short &port) throw() { sockaddr_in in4 = {0}; #ifdef _WIN32 int in4len = sizeof(in4); #else socklen_t in4len = sizeof(in4); #endif int result = ::getsockname(s,(sockaddr*)&in4,&in4len); if (result != socketOps::cSOCKET_ERROR) { port = ntohs(in4.sin_port); address = inet_ntoa(in4.sin_addr); } return result; } int socketOps::getsockname(tSOCKET s,endpoint &e) throw() { return socketOps::getsockname(s, e.m_address, e.m_port); } void socketOps::getsocknameTHROW(tSOCKET s, endpoint &e) throw(std::runtime_error) { if (socketOps::getsockname(s, e) == socketOps::cSOCKET_ERROR) { throw std::runtime_error(socketOps::errMsg()); } } void socketOps::getsocknameTHROW(tSOCKET s, std::string &address, u_short &port) throw(std::runtime_error) { if (socketOps::getsockname(s, address, port) == socketOps::cSOCKET_ERROR) { throw std::runtime_error(socketOps::errMsg()); } } int socketOps::getpeername(tSOCKET s, std::string &address, u_short &port) throw() { address = ""; port = 0; sockaddr_in in4 = {0}; #ifdef _WIN32 int in4len = sizeof(in4); #else socklen_t in4len = sizeof(in4); #endif int result = ::getpeername(s, (sockaddr*)&in4,&in4len); if (result != socketOps::cSOCKET_ERROR) { port = ntohs(in4.sin_port); address = inet_ntoa(in4.sin_addr); } return result; } int socketOps::getpeername(tSOCKET s, endpoint &e) throw() { return socketOps::getpeername(s, e.m_address, e.m_port); } void socketOps::getpeernameTHROW(tSOCKET s, endpoint &e) throw(std::runtime_error) { if (socketOps::getpeername(s, e) == socketOps::cSOCKET_ERROR) { throw std::runtime_error(socketOps::errMsg()); } } void socketOps::getpeernameTHROW(tSOCKET s, std::string &address, u_short &port) throw(std::runtime_error) { if (socketOps::getpeername(s, address, port) == socketOps::cSOCKET_ERROR) { throw std::runtime_error(socketOps::errMsg()); } } // accepts IPv4 or IPv6 int socketOps::addressToHostName(const std::string &address, u_short port, std::string &hostname) throw() { struct addrinfo *result/*, hints = {AI_PASSIVE | AI_CANONNAME, AF_INET, SOCK_STREAM}*/; std::string portS = tos(port); int err = ::getaddrinfo(address.c_str(),portS.c_str(),0,&result); if ((!err) && result) { char h[NI_MAXHOST + 1] = {0}; err = ::getnameinfo(result->ai_addr, (int)result->ai_addrlen, h, NI_MAXHOST, 0, 0, 0); hostname = h; ::freeaddrinfo(result); } return err; } int socketOps::addressToHostName(const endpoint &e, std::string &hostname) throw() { return socketOps::addressToHostName(e.m_address, e.m_port, hostname); } void socketOps::addressToHostNameTHROW(const std::string &address, u_short port, std::string &hostname) throw(std::runtime_error) { if (socketOps::addressToHostName(address, port, hostname) == socketOps::cSOCKET_ERROR) { throw std::runtime_error(socketOps::errMsg()); } } void socketOps::addressToHostNameTHROW(const endpoint &e, std::string &hostname) throw(std::runtime_error) { socketOps::addressToHostNameTHROW(e.m_address, e.m_port, hostname); } int socketOps::hostNameToAddress(std::string &address, const std::string &hostname, u_short port) throw(std::runtime_error) { struct addrinfo *result, hints = {AI_PASSIVE | AI_CANONNAME, AF_INET, SOCK_STREAM}; address = ""; std::string portS = tos(port); int err = ::getaddrinfo(hostname.c_str(), (port ? portS.c_str() : 0), &hints, &result); if ((!err) && result) { address = ::inet_ntoa(((sockaddr_in*)(result->ai_addr))->sin_addr); if (!address.empty() && address.find("0.") == 0) { address = ""; } ::freeaddrinfo(result); } return err; } std::string socketOps::hostNameToAddress(const std::string &hostname, u_short port) throw(std::runtime_error) { std::string address; hostNameToAddress(address, hostname, port); return address; } std::string socketOps::hostNameToAddressTHROW(const std::string &hostname, u_short port) throw(std::runtime_error) { std::string address; if (hostNameToAddress(address, hostname, port)) { throw std::runtime_error(socketOps::errMsg()); } return address; } #ifdef _WIN32 LPFN_WSAPOLL fnWSAPoll = NULL; typedef unsigned long nfds_t; static void add_to_fd_set_fntr(const socketOps::tSOCKET s, fd_set *fdset) throw() { FD_SET(s, fdset); } #else #define fnWSAPoll poll #endif #include int socketOps::socketSelect(std::set &readSockets, std::set writeSockets, const int timeout) throw() { //ELOG("socketSelect: " + tos(readSet.size()) + " - " + tos(writeSet.size()) + " - " + tos(timeout)); #ifdef _WIN32 if (!fnWSAPoll) { fd_set readSet; fd_set writeSet; fd_set excepSet; FD_ZERO(&readSet); FD_ZERO(&writeSet); FD_ZERO(&excepSet); for_each(readSockets.begin(), readSockets.end(), std::bind2nd(std::ptr_fun(add_to_fd_set_fntr), &readSet)); for_each(writeSockets.begin(), writeSockets.end(), std::bind2nd(std::ptr_fun(add_to_fd_set_fntr), &writeSet)); for_each(readSockets.begin(), readSockets.end(), std::bind2nd(std::ptr_fun(add_to_fd_set_fntr), &excepSet)); for_each(writeSockets.begin(), writeSockets.end(), std::bind2nd(std::ptr_fun(add_to_fd_set_fntr), &excepSet)); // as we're workig in milliseconds then we need to convert to // provide select the timeout that is wanted as a timeval struct timeval tm = {(timeout / 1000), ((timeout % 1000) * 1000)}; // The Windows version ignores the first arg, no need to calculate it return ::select(0, &readSet, &writeSet, &excepSet, &tm); } #endif size_t i = 0; nfds_t length = (nfds_t)(readSockets.size() + writeSockets.size()); std::vector dataSet (length); for (std::set::iterator it = readSockets.begin(); it != readSockets.end(); ++it) { dataSet[i].events = POLLIN; dataSet[i++].fd = *it; } for (std::set::iterator it = writeSockets.begin(); it != writeSockets.end(); ++it) { dataSet[i].events = POLLOUT; dataSet[i++].fd = *it; } return fnWSAPoll (&dataSet[0], length, timeout); } #ifdef _WIN32 /*------------------------------------------------------------------------- * * This is a replacement version of pipe for Win32 which allows * returned handles to be used in select(). Note that read/write calls * must be replaced with recv/send. * * *------------------------------------------------------------------------- */ int pgpipe(int handles[2]) { SOCKET s = {0}; struct sockaddr_in serv_addr = {0}; int len = sizeof(serv_addr); handles[0] = handles[1] = (int)INVALID_SOCKET; if ((s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) { return -1; } serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(0); serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); if (::bind(s, (SOCKADDR *)&serv_addr, len) == SOCKET_ERROR) { ::closesocket(s); return -1; } if (::listen(s, 1) == SOCKET_ERROR) { ::closesocket(s); return -1; } if (::getsockname(s, (SOCKADDR *)&serv_addr, &len) == SOCKET_ERROR) { ::closesocket(s); return -1; } if ((handles[1] = (int)::socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) { ::closesocket(s); return -1; } if (::connect(handles[1], (SOCKADDR *)&serv_addr, len) == socketOps::cSOCKET_ERROR) { ::closesocket(s); return -1; } if ((handles[0] = (int)::accept(s, (SOCKADDR *)&serv_addr, &len)) == INVALID_SOCKET) { ::closesocket(handles[1]); handles[1] = (int)INVALID_SOCKET; ::closesocket(s); return -1; } ::closesocket(s); return 0; } #endif