// ********************************************************************** // // Copyright (c) 2003 - 2004 // ZeroC, Inc. // North Palm Beach, FL, USA // // All Rights Reserved. // // This copy of Ice is licensed to you under the terms described in the // ICE_LICENSE file included in this distribution. // // ********************************************************************** #include #include using namespace std; using namespace Ice; using namespace IceInternal; #ifdef __sun # define INADDR_NONE -1 #endif bool IceInternal::interrupted() { #ifdef _WIN32 return WSAGetLastError() == WSAEINTR; #else # ifdef EPROTO return errno == EINTR || errno == EPROTO; # else return errno == EINTR; # endif #endif } bool IceInternal::acceptInterrupted() { if(interrupted()) { return true; } #ifdef _WIN32 int error = WSAGetLastError(); return error == WSAECONNABORTED || error == WSAECONNRESET || error == WSAETIMEDOUT; #else return errno == ECONNABORTED || errno == ECONNRESET || errno == ETIMEDOUT; #endif } bool IceInternal::noBuffers() { #ifdef _WIN32 int error = WSAGetLastError(); return error == WSAENOBUFS || error == WSAEFAULT; #else return errno == ENOBUFS; #endif } bool IceInternal::wouldBlock() { #ifdef _WIN32 return WSAGetLastError() == WSAEWOULDBLOCK; #else return errno == EAGAIN || errno == EWOULDBLOCK; #endif } bool IceInternal::connectFailed() { #ifdef _WIN32 int error = WSAGetLastError(); return error == WSAECONNREFUSED || error == WSAETIMEDOUT || error == WSAENETUNREACH || error == WSAECONNRESET || error == WSAESHUTDOWN || error == WSAECONNABORTED; #else return errno == ECONNREFUSED || errno == ETIMEDOUT || errno == ENETUNREACH || errno == ECONNRESET || errno == ESHUTDOWN || errno == ECONNABORTED; #endif } bool IceInternal::connectionRefused() { #ifdef _WIN32 int error = WSAGetLastError(); return error == WSAECONNREFUSED; #else return errno == ECONNREFUSED; #endif } bool IceInternal::connectInProgress() { #ifdef _WIN32 return WSAGetLastError() == WSAEWOULDBLOCK; #else return errno == EINPROGRESS; #endif } bool IceInternal::connectionLost() { #ifdef _WIN32 int error = WSAGetLastError(); return error == WSAECONNRESET || error == WSAESHUTDOWN || error == WSAENOTCONN || error == WSAECONNABORTED; #else return errno == ECONNRESET || errno == ENOTCONN || errno == ESHUTDOWN || errno == ECONNABORTED || errno == EPIPE; #endif } bool IceInternal::notConnected() { #ifdef _WIN32 return WSAGetLastError() == WSAENOTCONN; #else return errno == ENOTCONN; #endif } bool IceInternal::recvTruncated() { #ifdef _WIN32 return WSAGetLastError() == WSAEMSGSIZE; #else // We don't get an error under Linux if a datagram is truncated. return false; #endif } SOCKET IceInternal::createSocket(bool udp) { SOCKET fd; if(udp) { fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); } else { fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); } if(fd == INVALID_SOCKET) { SocketException ex(__FILE__, __LINE__); ex.error = getSocketErrno(); throw ex; } if(!udp) { setTcpNoDelay(fd); setKeepAlive(fd); } return fd; } void IceInternal::closeSocket(SOCKET fd) { #ifdef _WIN32 int error = WSAGetLastError(); if(closesocket(fd) == SOCKET_ERROR) { SocketException ex(__FILE__, __LINE__); ex.error = getSocketErrno(); throw ex; } WSASetLastError(error); #else int error = errno; if(close(fd) == SOCKET_ERROR) { SocketException ex(__FILE__, __LINE__); ex.error = getSocketErrno(); throw ex; } errno = error; #endif } void IceInternal::shutdownSocket(SOCKET fd) { if(shutdown(fd, SHUT_WR) == SOCKET_ERROR) { // // Ignore errors indicating that we are shutdown already. // #ifdef _WIN32 int error = WSAGetLastError(); if(error == WSAENOTCONN) { return; } #else if(errno == ENOTCONN) { return; } #endif SocketException ex(__FILE__, __LINE__); ex.error = getSocketErrno(); throw ex; } } void IceInternal::setBlock(SOCKET fd, bool block) { if(block) { #ifdef _WIN32 unsigned long arg = 0; ioctlsocket(fd, FIONBIO, &arg); #else int flags = fcntl(fd, F_GETFL); flags &= ~O_NONBLOCK; fcntl(fd, F_SETFL, flags); #endif } else { #ifdef _WIN32 unsigned long arg = 1; ioctlsocket(fd, FIONBIO, &arg); #else int flags = fcntl(fd, F_GETFL); flags |= O_NONBLOCK; fcntl(fd, F_SETFL, flags); #endif } } void IceInternal::setTcpNoDelay(SOCKET fd) { int flag = 1; if(setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*)&flag, int(sizeof(int))) == SOCKET_ERROR) { closeSocket(fd); SocketException ex(__FILE__, __LINE__); ex.error = getSocketErrno(); throw ex; } } void IceInternal::setKeepAlive(SOCKET fd) { int flag = 1; if(setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char*)&flag, int(sizeof(int))) == SOCKET_ERROR) { closeSocket(fd); SocketException ex(__FILE__, __LINE__); ex.error = getSocketErrno(); throw ex; } } void IceInternal::setSendBufferSize(SOCKET fd, int sz) { if(setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char*)&sz, int(sizeof(int))) == SOCKET_ERROR) { closeSocket(fd); SocketException ex(__FILE__, __LINE__); ex.error = getSocketErrno(); throw ex; } } int IceInternal::getSendBufferSize(SOCKET fd) { int sz; socklen_t len = sizeof(sz); if(getsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char*)&sz, &len) == SOCKET_ERROR || len != sizeof(sz)) { closeSocket(fd); SocketException ex(__FILE__, __LINE__); ex.error = getSocketErrno(); throw ex; } return sz; } void IceInternal::setRecvBufferSize(SOCKET fd, int sz) { if(setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char*)&sz, int(sizeof(int))) == SOCKET_ERROR) { closeSocket(fd); SocketException ex(__FILE__, __LINE__); ex.error = getSocketErrno(); throw ex; } } int IceInternal::getRecvBufferSize(SOCKET fd) { int sz; socklen_t len = sizeof(sz); if(getsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char*)&sz, &len) == SOCKET_ERROR || len != sizeof(sz)) { closeSocket(fd); SocketException ex(__FILE__, __LINE__); ex.error = getSocketErrno(); throw ex; } return sz; } void IceInternal::doBind(SOCKET fd, struct sockaddr_in& addr) { #ifndef _WIN32 int flag = 1; if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&flag, int(sizeof(int))) == SOCKET_ERROR) { closeSocket(fd); SocketException ex(__FILE__, __LINE__); ex.error = getSocketErrno(); throw ex; } #endif if(bind(fd, reinterpret_cast(&addr), int(sizeof(addr))) == SOCKET_ERROR) { closeSocket(fd); SocketException ex(__FILE__, __LINE__); ex.error = getSocketErrno(); throw ex; } socklen_t len = static_cast(sizeof(addr)); #ifdef NDEBUG getsockname(fd, reinterpret_cast(&addr), &len); #else int ret = getsockname(fd, reinterpret_cast(&addr), &len); assert(ret != SOCKET_ERROR); #endif } void IceInternal::doListen(SOCKET fd, int backlog) { repeatListen: if(::listen(fd, backlog) == SOCKET_ERROR) { if(interrupted()) { goto repeatListen; } closeSocket(fd); SocketException ex(__FILE__, __LINE__); ex.error = getSocketErrno(); throw ex; } } void IceInternal::doConnect(SOCKET fd, struct sockaddr_in& addr, int timeout) { #ifdef _WIN32 // // Set larger send buffer size to avoid performance problems on // WIN32. // setSendBufferSize(fd, 64 * 1024); #endif repeatConnect: if(::connect(fd, reinterpret_cast(&addr), int(sizeof(addr))) == SOCKET_ERROR) { if(interrupted()) { goto repeatConnect; } if(connectInProgress()) { repeatSelect: int ret; fd_set wFdSet; FD_ZERO(&wFdSet); FD_SET(fd, &wFdSet); #ifdef _WIN32 // // WIN32 notifies about connection failures // through the exception filedescriptors // fd_set xFdSet; FD_ZERO(&xFdSet); FD_SET(fd, &xFdSet); #endif if(timeout >= 0) { struct timeval tv; tv.tv_sec = timeout / 1000; tv.tv_usec = (timeout - tv.tv_sec * 1000) * 1000; #ifdef _WIN32 ret = ::select(fd + 1, 0, &wFdSet, &xFdSet, &tv); #else ret = ::select(fd + 1, 0, &wFdSet, 0, &tv); #endif } else { #ifdef _WIN32 ret = ::select(fd + 1, 0, &wFdSet, &xFdSet, 0); #else ret = ::select(fd + 1, 0, &wFdSet, 0, 0); #endif } if(ret == 0) { closeSocket(fd); throw ConnectTimeoutException(__FILE__, __LINE__); } else if(ret == SOCKET_ERROR) { if(interrupted()) { goto repeatSelect; } SocketException ex(__FILE__, __LINE__); ex.error = getSocketErrno(); throw ex; } #ifdef _WIN32 // // Strange windows bug: The following call to Sleep() is // necessary, otherwise no error is reported through // getsockopt. // Sleep(0); #endif socklen_t len = static_cast(sizeof(int)); int val; if(getsockopt(fd, SOL_SOCKET, SO_ERROR, reinterpret_cast(&val), &len) == SOCKET_ERROR) { closeSocket(fd); SocketException ex(__FILE__, __LINE__); ex.error = getSocketErrno(); throw ex; } if(val > 0) { closeSocket(fd); #ifdef _WIN32 WSASetLastError(val); #else errno = val; #endif if(connectionRefused()) { ConnectionRefusedException ex(__FILE__, __LINE__); ex.error = getSocketErrno(); throw ex; } else if(connectFailed()) { ConnectFailedException ex(__FILE__, __LINE__); ex.error = getSocketErrno(); throw ex; } else { SocketException ex(__FILE__, __LINE__); ex.error = getSocketErrno(); throw ex; } } return; } closeSocket(fd); if(connectionRefused()) { ConnectionRefusedException ex(__FILE__, __LINE__); ex.error = getSocketErrno(); throw ex; } else if(connectFailed()) { ConnectFailedException ex(__FILE__, __LINE__); ex.error = getSocketErrno(); throw ex; } else { SocketException ex(__FILE__, __LINE__); ex.error = getSocketErrno(); throw ex; } } } SOCKET IceInternal::doAccept(SOCKET fd, int timeout) { int ret; repeatAccept: if((ret = ::accept(fd, 0, 0)) == INVALID_SOCKET) { if(acceptInterrupted()) { goto repeatAccept; } if(wouldBlock()) { repeatSelect: int rs; fd_set fdSet; FD_ZERO(&fdSet); FD_SET(fd, &fdSet); if(timeout >= 0) { struct timeval tv; tv.tv_sec = timeout / 1000; tv.tv_usec = (timeout - tv.tv_sec * 1000) * 1000; rs = ::select(fd + 1, 0, &fdSet, 0, &tv); } else { rs = ::select(fd + 1, 0, &fdSet, 0, 0); } if(rs == SOCKET_ERROR) { if(interrupted()) { goto repeatSelect; } SocketException ex(__FILE__, __LINE__); ex.error = getSocketErrno(); throw ex; } if(rs == 0) { throw TimeoutException(__FILE__, __LINE__); } goto repeatAccept; } SocketException ex(__FILE__, __LINE__); ex.error = getSocketErrno(); throw ex; } setTcpNoDelay(ret); setKeepAlive(ret); #ifdef _WIN32 // // Set larger send buffer size to avoid performance problems on // WIN32. // setSendBufferSize(ret, 64 * 1024); #endif return ret; } void IceInternal::getAddress(const string& host, int port, struct sockaddr_in& addr) { memset(&addr, 0, sizeof(struct sockaddr_in)); addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = inet_addr(host.c_str()); if(addr.sin_addr.s_addr == INADDR_NONE) { #ifdef _WIN32 // // Windows XP has getaddrinfo(), but we don't want to require XP to run Ice. // // // gethostbyname() is thread safe on Windows, with a separate hostent per thread // struct hostent* entry; int retry = 5; do { entry = gethostbyname(host.c_str()); } while(entry == 0 && WSAGetLastError() == WSATRY_AGAIN && --retry >= 0); if(entry == 0) { DNSException ex(__FILE__, __LINE__); ex.error = WSAGetLastError(); ex.host = host; throw ex; } memcpy(&addr.sin_addr, entry->h_addr, entry->h_length); #else struct addrinfo* info = 0; int retry = 5; struct addrinfo hints = { 0 }; hints.ai_family = PF_INET; int rs = 0; do { rs = getaddrinfo(host.c_str(), 0, &hints, &info); } while(info == 0 && rs == EAI_AGAIN && --retry >= 0); if(rs != 0) { DNSException ex(__FILE__, __LINE__); ex.error = rs; ex.host = host; throw ex; } assert(info->ai_family == PF_INET); struct sockaddr_in* sin = reinterpret_cast(info->ai_addr); addr.sin_addr.s_addr = sin->sin_addr.s_addr; freeaddrinfo(info); #endif } } string IceInternal::getLocalHost(bool numeric) { char host[1024 + 1]; if(gethostname(host, 1024) == SOCKET_ERROR) { SyscallException ex(__FILE__, __LINE__); ex.error = getSystemErrno(); throw ex; } #ifdef _WIN32 // // Windows XP has getaddrinfo(), but we don't want to require XP to run Ice. // // // gethostbyname() is thread safe on Windows, with a separate hostent per thread // struct hostent* entry; int retry = 5; do { entry = gethostbyname(host); } while(entry == 0 && WSAGetLastError() == WSATRY_AGAIN && --retry >= 0); if(entry == 0) { DNSException ex(__FILE__, __LINE__); ex.error = WSAGetLastError(); ex.host = host; throw ex; } if(numeric) { struct sockaddr_in addr; memset(&addr, 0, sizeof(struct sockaddr_in)); memcpy(&addr.sin_addr, entry->h_addr, entry->h_length); return string(inet_ntoa(addr.sin_addr)); } else { return string(entry->h_name); } #else struct addrinfo* info = 0; int retry = 5; struct addrinfo hints = { 0 }; hints.ai_family = PF_INET; int rs = 0; do { rs = getaddrinfo(host, 0, &hints, &info); } while(info == 0 && rs == EAI_AGAIN && --retry >= 0); if(rs != 0) { DNSException ex(__FILE__, __LINE__); ex.error = rs; ex.host = host; throw ex; } string result; if(numeric) { assert(info->ai_family == PF_INET); struct sockaddr_in* sin = reinterpret_cast(info->ai_addr); result = inet_ntoa(sin->sin_addr); } else { result = info->ai_canonname; } freeaddrinfo(info); return result; #endif } bool IceInternal::compareAddress(const struct sockaddr_in& addr1, const struct sockaddr_in& addr2) { return (addr1.sin_family == addr2.sin_family) && (addr1.sin_port == addr2.sin_port) && (addr1.sin_addr.s_addr == addr2.sin_addr.s_addr); } void IceInternal::createPipe(SOCKET fds[2]) { #ifdef _WIN32 SOCKET fd = createSocket(false); setBlock(fd, true); struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(0); addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); doBind(fd, addr); doListen(fd, 1); try { fds[0] = createSocket(false); } catch(...) { ::closesocket(fd); throw; } try { setBlock(fds[0], true); doConnect(fds[0], addr, -1); fds[1] = doAccept(fd, -1); } catch(...) { ::closesocket(fd); ::closesocket(fds[0]); throw; } ::closesocket(fd); try { setBlock(fds[1], true); } catch(...) { ::closesocket(fds[0]); ::closesocket(fds[1]); throw; } #else if(::pipe(fds) != 0) { SyscallException ex(__FILE__, __LINE__); ex.error = getSystemErrno(); throw ex; } try { setBlock(fds[0], true); setBlock(fds[1], true); } catch(...) { close(fds[0]); close(fds[1]); throw; } #endif } #ifdef _WIN32 string IceInternal::errorToString(int error) { if(error < WSABASEERR) { LPVOID lpMsgBuf = 0; DWORD ok = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language (LPTSTR)&lpMsgBuf, 0, NULL); if(ok) { LPCTSTR msg = (LPCTSTR)lpMsgBuf; assert(msg && strlen((const char*)msg) > 0); string result = (const char*)msg; LocalFree(lpMsgBuf); return result; } } switch(error) { case WSAEINTR: return "WSAEINTR"; case WSAEBADF: return "WSAEBADF"; case WSAEACCES: return "WSAEACCES"; case WSAEFAULT: return "WSAEFAULT"; case WSAEINVAL: return "WSAEINVAL"; case WSAEMFILE: return "WSAEMFILE"; case WSAEWOULDBLOCK: return "WSAEWOULDBLOCK"; case WSAEINPROGRESS: return "WSAEINPROGRESS"; case WSAEALREADY: return "WSAEALREADY"; case WSAENOTSOCK: return "WSAENOTSOCK"; case WSAEDESTADDRREQ: return "WSAEDESTADDRREQ"; case WSAEMSGSIZE: return "WSAEMSGSIZE"; case WSAEPROTOTYPE: return "WSAEPROTOTYPE"; case WSAENOPROTOOPT: return "WSAENOPROTOOPT"; case WSAEPROTONOSUPPORT: return "WSAEPROTONOSUPPORT"; case WSAESOCKTNOSUPPORT: return "WSAESOCKTNOSUPPORT"; case WSAEOPNOTSUPP: return "WSAEOPNOTSUPP"; case WSAEPFNOSUPPORT: return "WSAEPFNOSUPPORT"; case WSAEAFNOSUPPORT: return "WSAEAFNOSUPPORT"; case WSAEADDRINUSE: return "WSAEADDRINUSE"; case WSAEADDRNOTAVAIL: return "WSAEADDRNOTAVAIL"; case WSAENETDOWN: return "WSAENETDOWN"; case WSAENETUNREACH: return "WSAENETUNREACH"; case WSAENETRESET: return "WSAENETRESET"; case WSAECONNABORTED: return "WSAECONNABORTED"; case WSAECONNRESET: return "WSAECONNRESET"; case WSAENOBUFS: return "WSAENOBUFS"; case WSAEISCONN: return "WSAEISCONN"; case WSAENOTCONN: return "WSAENOTCONN"; case WSAESHUTDOWN: return "WSAESHUTDOWN"; case WSAETOOMANYREFS: return "WSAETOOMANYREFS"; case WSAETIMEDOUT: return "WSAETIMEDOUT"; case WSAECONNREFUSED: return "WSAECONNREFUSED"; case WSAELOOP: return "WSAELOOP"; case WSAENAMETOOLONG: return "WSAENAMETOOLONG"; case WSAEHOSTDOWN: return "WSAEHOSTDOWN"; case WSAEHOSTUNREACH: return "WSAEHOSTUNREACH"; case WSAENOTEMPTY: return "WSAENOTEMPTY"; case WSAEPROCLIM: return "WSAEPROCLIM"; case WSAEUSERS: return "WSAEUSERS"; case WSAEDQUOT: return "WSAEDQUOT"; case WSAESTALE: return "WSAESTALE"; case WSAEREMOTE: return "WSAEREMOTE"; case WSAEDISCON: return "WSAEDISCON"; case WSASYSNOTREADY: return "WSASYSNOTREADY"; case WSAVERNOTSUPPORTED: return "WSAVERNOTSUPPORTED"; case WSANOTINITIALISED: return "WSANOTINITIALISED"; case WSAHOST_NOT_FOUND: return "WSAHOST_NOT_FOUND"; case WSATRY_AGAIN: return "WSATRY_AGAIN"; case WSANO_RECOVERY: return "WSANO_RECOVERY"; case WSANO_DATA: return "WSANO_DATA"; default: return "unknown socket error"; } } string IceInternal::errorToStringDNS(int error) { return errorToString(error); } #else string IceInternal::errorToString(int error) { return strerror(error); } string IceInternal::errorToStringDNS(int error) { return gai_strerror(error); } #endif string IceInternal::lastErrorToString() { #ifdef _WIN32 return errorToString(WSAGetLastError()); #else return errorToString(errno); #endif } std::string IceInternal::fdToString(SOCKET fd) { if(fd == INVALID_SOCKET) { return ""; } socklen_t localLen = static_cast(sizeof(struct sockaddr_in)); struct sockaddr_in localAddr; if(getsockname(fd, reinterpret_cast(&localAddr), &localLen) == SOCKET_ERROR) { closeSocket(fd); SocketException ex(__FILE__, __LINE__); ex.error = getSocketErrno(); throw ex; } bool peerNotConnected = false; socklen_t remoteLen = static_cast(sizeof(struct sockaddr_in)); struct sockaddr_in remoteAddr; if(getpeername(fd, reinterpret_cast(&remoteAddr), &remoteLen) == SOCKET_ERROR) { if(notConnected()) { peerNotConnected = true; } else { closeSocket(fd); SocketException ex(__FILE__, __LINE__); ex.error = getSocketErrno(); throw ex; } } ostringstream s; s << "local address = " << inet_ntoa(localAddr.sin_addr) << ':' << ntohs(localAddr.sin_port); if(peerNotConnected) { s << "\nremote address = "; } else { s << "\nremote address = " << inet_ntoa(remoteAddr.sin_addr) << ':' << ntohs(remoteAddr.sin_port); } return s.str(); } std::string IceInternal::addrToString(const struct sockaddr_in& addr) { // // inet_ntoa uses thread-specific data on Windows, Linux, Solaris // and HP-UX // ostringstream s; s << inet_ntoa(addr.sin_addr) << ':' << ntohs(addr.sin_port); return s.str(); }