diff options
Diffstat (limited to 'cpp/src/Ice/Network.cpp')
-rw-r--r-- | cpp/src/Ice/Network.cpp | 2028 |
1 files changed, 2028 insertions, 0 deletions
diff --git a/cpp/src/Ice/Network.cpp b/cpp/src/Ice/Network.cpp new file mode 100644 index 00000000000..e3ea91d6e17 --- /dev/null +++ b/cpp/src/Ice/Network.cpp @@ -0,0 +1,2028 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2011 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +// +// The following is required on HP-UX in order to bring in +// the definition for the ip_mreq structure. +// +#if defined(__hpux) +# undef _XOPEN_SOURCE_EXTENDED +# define _XOPEN_SOURCE +# include <netinet/in.h> +#endif + +// +// The following is required for the Vista PSDK to bring in +// the definitions of the IN6_IS_ADDR_* macros. +// +#if defined(_WIN32) && !defined(_WIN32_WINNT) +# define _WIN32_WINNT 0x0501 +#endif + +#include <IceUtil/StringUtil.h> +#include <IceUtil/Unicode.h> +#include <Ice/Network.h> +#include <Ice/LocalException.h> +#include <Ice/Properties.h> // For setTcpBufSize +#include <Ice/LoggerUtil.h> // For setTcpBufSize + +#if defined(_WIN32) +# include <winsock2.h> +# include <ws2tcpip.h> +# include <iphlpapi.h> +# include <Mswsock.h> +#else +# include <net/if.h> +# include <sys/ioctl.h> +#endif + +#if defined(__linux) || defined(__APPLE__) || defined(__FreeBSD__) +# include <ifaddrs.h> +#elif defined(__sun) +# include <sys/sockio.h> +#endif + +using namespace std; +using namespace Ice; +using namespace IceInternal; + +#if defined(__sun) && !defined(__GNUC__) +# define INADDR_NONE (in_addr_t)0xffffffff +#endif + +namespace +{ + +vector<struct sockaddr_storage> +getLocalAddresses(ProtocolSupport protocol) +{ + vector<struct sockaddr_storage> result; + +#if defined(_WIN32) + try + { + for(int i = 0; i < 2; i++) + { + if((i == 0 && protocol == EnableIPv6) || (i == 1 && protocol == EnableIPv4)) + { + continue; + } + + SOCKET fd = createSocket(false, i == 0 ? AF_INET : AF_INET6); + + vector<unsigned char> buffer; + buffer.resize(1024); + unsigned long len = 0; + int rs = WSAIoctl(fd, SIO_ADDRESS_LIST_QUERY, 0, 0, + &buffer[0], static_cast<DWORD>(buffer.size()), + &len, 0, 0); + if(rs == SOCKET_ERROR) + { + // + // If the buffer wasn't big enough, resize it to the + // required length and try again. + // + if(getSocketErrno() == WSAEFAULT) + { + buffer.resize(len); + rs = WSAIoctl(fd, SIO_ADDRESS_LIST_QUERY, 0, 0, + &buffer[0], static_cast<DWORD>(buffer.size()), + &len, 0, 0); + } + + if(rs == SOCKET_ERROR) + { + closeSocketNoThrow(fd); + SocketException ex(__FILE__, __LINE__); + ex.error = getSocketErrno(); + throw ex; + } + } + + // + // Add the local interface addresses. + // + SOCKET_ADDRESS_LIST* addrs = reinterpret_cast<SOCKET_ADDRESS_LIST*>(&buffer[0]); + for (int i = 0; i < addrs->iAddressCount; ++i) + { + sockaddr_storage addr; + memcpy(&addr, addrs->Address[i].lpSockaddr, addrs->Address[i].iSockaddrLength); + if(addr.ss_family == AF_INET && protocol != EnableIPv6) + { + if(reinterpret_cast<struct sockaddr_in*>(&addr)->sin_addr.s_addr != 0) + { + result.push_back(addr); + } + } + else if(addr.ss_family == AF_INET6 && protocol != EnableIPv4) + { + struct in6_addr* inaddr6 = &reinterpret_cast<struct sockaddr_in6*>(&addr)->sin6_addr; + if(!IN6_IS_ADDR_UNSPECIFIED(inaddr6) && !IN6_IS_ADDR_LOOPBACK(inaddr6)) + { + result.push_back(addr); + } + } + } + + closeSocket(fd); + } + } + catch(const Ice::LocalException&) + { + // + // TODO: Warning? + // + } +#elif defined(__linux) || defined(__APPLE__) || defined(__FreeBSD__) + struct ifaddrs* ifap; + if(::getifaddrs(&ifap) == SOCKET_ERROR) + { + SocketException ex(__FILE__, __LINE__); + ex.error = getSocketErrno(); + throw ex; + } + + struct ifaddrs* curr = ifap; + while(curr != 0) + { + if(curr->ifa_addr && !(curr->ifa_flags & IFF_LOOPBACK)) // Don't include loopback interface addresses + { + if(curr->ifa_addr->sa_family == AF_INET && protocol != EnableIPv6) + { + sockaddr_storage addr; + memcpy(&addr, curr->ifa_addr, sizeof(sockaddr_in)); + if(reinterpret_cast<struct sockaddr_in*>(&addr)->sin_addr.s_addr != 0) + { + result.push_back(addr); + } + } + else if(curr->ifa_addr->sa_family == AF_INET6 && protocol != EnableIPv4) + { + sockaddr_storage addr; + memcpy(&addr, curr->ifa_addr, sizeof(sockaddr_in6)); + if(!IN6_IS_ADDR_UNSPECIFIED(&reinterpret_cast<struct sockaddr_in6*>(&addr)->sin6_addr)) + { + result.push_back(*reinterpret_cast<struct sockaddr_storage*>(curr->ifa_addr)); + } + } + } + + curr = curr->ifa_next; + } + + ::freeifaddrs(ifap); +#else + for(int i = 0; i < 2; i++) + { + if((i == 0 && protocol == EnableIPv6) || (i == 1 && protocol == EnableIPv4)) + { + continue; + } + SOCKET fd = createSocket(false, i == 0 ? AF_INET : AF_INET6); + +#ifdef _AIX + int cmd = CSIOCGIFCONF; +#else + int cmd = SIOCGIFCONF; +#endif + struct ifconf ifc; + int numaddrs = 10; + int old_ifc_len = 0; + + // + // Need to call ioctl multiple times since we do not know up front + // how many addresses there will be, and thus how large a buffer we need. + // We keep increasing the buffer size until subsequent calls return + // the same length, meaning we have all the addresses. + // + while(true) + { + int bufsize = numaddrs * static_cast<int>(sizeof(struct ifreq)); + ifc.ifc_len = bufsize; + ifc.ifc_buf = (char*)malloc(bufsize); + + int rs = ioctl(fd, cmd, &ifc); + if(rs == SOCKET_ERROR) + { + free(ifc.ifc_buf); + closeSocketNoThrow(fd); + SocketException ex(__FILE__, __LINE__); + ex.error = getSocketErrno(); + throw ex; + } + else if(ifc.ifc_len == old_ifc_len) + { + // + // Returned same length twice in a row, finished. + // + break; + } + else + { + old_ifc_len = ifc.ifc_len; + } + + numaddrs += 10; + free(ifc.ifc_buf); + } + closeSocket(fd); + + numaddrs = ifc.ifc_len / static_cast<int>(sizeof(struct ifreq)); + struct ifreq* ifr = ifc.ifc_req; + for(int i = 0; i < numaddrs; ++i) + { + if(!(ifr[i].ifr_flags & IFF_LOOPBACK)) // Don't include loopback interface addresses + { + // + // On Solaris the above Loopback check does not always work so we double + // check the address below. Solaris also returns duplicate entries that need + // to be filtered out. + // + if(ifr[i].ifr_addr.sa_family == AF_INET && protocol != EnableIPv6) + { + sockaddr_storage addr; + memcpy(&addr, &ifr[i].ifr_addr, sizeof(sockaddr_in)); + struct in_addr* inaddr = &reinterpret_cast<struct sockaddr_in*>(&addr)->sin_addr; + if(inaddr->s_addr != 0 && inaddr->s_addr != htonl(INADDR_LOOPBACK)) + { + unsigned int j; + for(j = 0; j < result.size(); ++j) + { + if(compareAddress(addr, result[j]) == 0) + { + break; + } + } + if(j == result.size()) + { + result.push_back(addr); + } + } + } + else if(ifr[i].ifr_addr.sa_family == AF_INET6 && protocol != EnableIPv4) + { + sockaddr_storage addr; + memcpy(&addr, &ifr[i].ifr_addr, sizeof(sockaddr_in6)); + struct in6_addr* inaddr6 = &reinterpret_cast<struct sockaddr_in6*>(&addr)->sin6_addr; + if(!IN6_IS_ADDR_UNSPECIFIED(inaddr6) && !IN6_IS_ADDR_LOOPBACK(inaddr6)) + { + unsigned int j; + for(j = 0; j < result.size(); ++j) + { + if(compareAddress(addr, result[j]) == 0) + { + break; + } + } + if(j == result.size()) + { + result.push_back(addr); + } + } + } + } + } + free(ifc.ifc_buf); + } +#endif + + return result; +} + +struct sockaddr_storage +getAddressImpl(const string& host, int port, ProtocolSupport protocol, bool server) +{ + struct sockaddr_storage addr; + memset(&addr, 0, sizeof(struct sockaddr_storage)); + + // + // We don't use getaddrinfo when host is empty as it's not portable (some old Linux + // versions don't support it). + // + if(host.empty()) + { + if(protocol == EnableIPv6) + { + sockaddr_in6* addrin6 = reinterpret_cast<sockaddr_in6*>(&addr); + addrin6->sin6_family = AF_INET6; + addrin6->sin6_port = htons(port); + addrin6->sin6_addr = server ? in6addr_any : in6addr_loopback; + } + else + { + sockaddr_in* addrin = reinterpret_cast<sockaddr_in*>(&addr); + addrin->sin_family = AF_INET; + addrin->sin_port = htons(port); + addrin->sin_addr.s_addr = server ? htonl(INADDR_ANY) : htonl(INADDR_LOOPBACK); + } + return addr; + } + + struct addrinfo* info = 0; + int retry = 5; + + struct addrinfo hints = { 0 }; + + if(server) + { + // + // If host is empty, getaddrinfo will return the wildcard + // address instead of the loopack address. + // + hints.ai_flags |= AI_PASSIVE; + } + + if(protocol == EnableIPv4) + { + hints.ai_family = PF_INET; + } + else if(protocol == EnableIPv6) + { + hints.ai_family = PF_INET6; + } + else + { + hints.ai_family = PF_UNSPEC; + } + + 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; + } + + memcpy(&addr, info->ai_addr, info->ai_addrlen); + if(info->ai_family == PF_INET) + { + reinterpret_cast<sockaddr_in*>(&addr)->sin_port = htons(port); + } + else if(info->ai_family == PF_INET6) + { + reinterpret_cast<sockaddr_in6*>(&addr)->sin6_port = htons(port); + } + else // Unknown address family. + { + freeaddrinfo(info); + DNSException ex(__FILE__, __LINE__); + ex.host = host; + throw ex; + } + freeaddrinfo(info); + return addr; +} + +bool +isWildcard(const string& host, ProtocolSupport protocol) +{ + try + { + sockaddr_storage addr = getAddressImpl(host, 0, protocol, false); + if(addr.ss_family == AF_INET) + { + struct sockaddr_in* addrin = reinterpret_cast<sockaddr_in*>(&addr); + if(addrin->sin_addr.s_addr == INADDR_ANY) + { + return true; + } + } + else if(addr.ss_family) + { + struct sockaddr_in6* addrin6 = reinterpret_cast<sockaddr_in6*>(&addr); + if(IN6_IS_ADDR_UNSPECIFIED(&addrin6->sin6_addr)) + { + return true; + } + } + } + catch(const DNSException&) + { + } + return false; +} + +int +getInterfaceIndex(const string& name) +{ + int index = 0; +#ifdef _WIN32 + IP_ADAPTER_ADDRESSES addrs; + ULONG buflen = 0; + if(::GetAdaptersAddresses(AF_INET6, 0, 0, &addrs, &buflen) == ERROR_BUFFER_OVERFLOW) + { + PIP_ADAPTER_ADDRESSES paddrs; + char* buf = new char[buflen]; + paddrs = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(buf); + if(::GetAdaptersAddresses(AF_INET6, 0, 0, paddrs, &buflen) == NO_ERROR) + { + while(paddrs) + { + if(IceUtil::wstringToString(paddrs->FriendlyName) == name) + { + index = paddrs->Ipv6IfIndex; + break; + } + paddrs = paddrs->Next; + } + } + delete[] buf; + } +#elif !defined(__hpux) + index = if_nametoindex(name.c_str()); +#endif + return index; +} + +struct in_addr +getInterfaceAddress(const string& name) +{ + struct in_addr addr; + addr.s_addr = INADDR_ANY; +#ifdef _WIN32 + IP_ADAPTER_ADDRESSES addrs; + ULONG buflen = 0; + if(::GetAdaptersAddresses(AF_INET, 0, 0, &addrs, &buflen) == ERROR_BUFFER_OVERFLOW) + { + PIP_ADAPTER_ADDRESSES paddrs; + char* buf = new char[buflen]; + paddrs = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(buf); + if(::GetAdaptersAddresses(AF_INET, 0, 0, paddrs, &buflen) == NO_ERROR) + { + while(paddrs) + { + if(IceUtil::wstringToString(paddrs->FriendlyName) == name) + { + struct sockaddr_in addrin; + memcpy(&addrin, paddrs->FirstUnicastAddress->Address.lpSockaddr, + paddrs->FirstUnicastAddress->Address.iSockaddrLength); + addr = addrin.sin_addr; + break; + } + paddrs = paddrs->Next; + } + } + delete[] buf; + } +#else + ifreq if_address; + strcpy(if_address.ifr_name, name.c_str()); + + SOCKET fd = createSocket(false, AF_INET); + int rc = ioctl(fd, SIOCGIFADDR, &if_address); + closeSocketNoThrow(fd); + + if(rc != SOCKET_ERROR) + { + addr = reinterpret_cast<struct sockaddr_in*>(&if_address.ifr_addr)->sin_addr; + } +#endif + + return addr; +} + +} + +#ifdef ICE_USE_IOCP +IceInternal::AsyncInfo::AsyncInfo(SocketOperation s) +{ + ZeroMemory(this, sizeof(AsyncInfo)); + status = s; +} +#endif + +int +IceInternal::getSocketErrno() +{ +#ifdef _WIN32 + return WSAGetLastError(); +#else + return errno; +#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 + int error = WSAGetLastError(); + return error == WSAEWOULDBLOCK || error == WSA_IO_PENDING || error == ERROR_IO_PENDING; +#else + return errno == EAGAIN || errno == EWOULDBLOCK; +#endif +} + +bool +IceInternal::connectFailed() +{ +#ifdef _WIN32 + int error = WSAGetLastError(); + return error == WSAECONNREFUSED || + error == WSAETIMEDOUT || + error == WSAENETUNREACH || + error == WSAEHOSTUNREACH || + error == WSAECONNRESET || + error == WSAESHUTDOWN || + error == WSAECONNABORTED; +#else + return errno == ECONNREFUSED || + errno == ETIMEDOUT || + errno == ENETUNREACH || + errno == EHOSTUNREACH || + errno == ECONNRESET || + errno == ESHUTDOWN || + errno == ECONNABORTED; +#endif +} + +bool +IceInternal::connectionRefused() +{ +#ifdef _WIN32 + int error = WSAGetLastError(); + return error == WSAECONNREFUSED || error == ERROR_CONNECTION_REFUSED; +#else + return errno == ECONNREFUSED; +#endif +} + +bool +IceInternal::connectInProgress() +{ +#ifdef _WIN32 + int error = WSAGetLastError(); + return error == WSAEWOULDBLOCK || error == WSA_IO_PENDING || error == ERROR_IO_PENDING; +#else + return errno == EINPROGRESS; +#endif +} + +bool +IceInternal::connectionLost() +{ +#ifdef _WIN32 + int error = WSAGetLastError(); + return error == WSAECONNRESET || + error == WSAESHUTDOWN || + error == WSAENOTCONN || +#ifdef ICE_USE_IOCP + error == ERROR_NETNAME_DELETED || +#endif + error == WSAECONNABORTED; +#else + return errno == ECONNRESET || + errno == ENOTCONN || + errno == ESHUTDOWN || + errno == ECONNABORTED || + errno == EPIPE; +#endif +} + +bool +IceInternal::notConnected() +{ +#ifdef _WIN32 + return WSAGetLastError() == WSAENOTCONN; +#elif defined(__APPLE__) || defined(__FreeBSD__) + return errno == ENOTCONN || errno == EINVAL; +#else + return errno == ENOTCONN; +#endif +} + +bool +IceInternal::recvTruncated() +{ +#ifdef _WIN32 + int err = WSAGetLastError(); + return err == WSAEMSGSIZE || err == ERROR_MORE_DATA; +#else + // We don't get an error under Linux if a datagram is truncated. + return false; +#endif +} + +bool +IceInternal::noMoreFds(int error) +{ +#ifdef _WIN32 + return error == WSAEMFILE; +#else + return error == EMFILE || error == ENFILE; +#endif +} + +SOCKET +IceInternal::createSocket(bool udp, int family) +{ + SOCKET fd; + + if(udp) + { + fd = socket(family, SOCK_DGRAM, IPPROTO_UDP); + } + else + { + fd = socket(family, 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::closeSocketNoThrow(SOCKET fd) +{ +#ifdef _WIN32 + int error = WSAGetLastError(); + closesocket(fd); + WSASetLastError(error); +#else + int error = errno; + close(fd); + errno = error; +#endif +} + +void +IceInternal::shutdownSocketWrite(SOCKET fd) +{ + if(shutdown(fd, SHUT_WR) == SOCKET_ERROR) + { + // + // Ignore errors indicating that we are shutdown already. + // +#if defined(_WIN32) + int error = WSAGetLastError(); + // + // Under Vista its possible to get a WSAECONNRESET. See + // http://bugzilla.zeroc.com/bugzilla/show_bug.cgi?id=1739 for + // some details. + // + if(error == WSAENOTCONN || error == WSAECONNRESET) + { + return; + } +#elif defined(__APPLE__) || defined(__FreeBSD__) + if(errno == ENOTCONN || errno == EINVAL) + { + return; + } +#else + if(errno == ENOTCONN) + { + return; + } +#endif + SocketException ex(__FILE__, __LINE__); + ex.error = getSocketErrno(); + throw ex; + } +} + +void +IceInternal::shutdownSocketReadWrite(SOCKET fd) +{ + if(shutdown(fd, SHUT_RDWR) == SOCKET_ERROR) + { + // + // Ignore errors indicating that we are shutdown already. + // +#if defined(_WIN32) + int error = WSAGetLastError(); + // + // Under Vista its possible to get a WSAECONNRESET. See + // http://bugzilla.zeroc.com/bugzilla/show_bug.cgi?id=1739 for + // some details. + // + if(error == WSAENOTCONN || error == WSAECONNRESET) + { + return; + } +#elif defined(__APPLE__) || defined(__FreeBSD__) + if(errno == ENOTCONN || errno == EINVAL) + { + 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; + if(ioctlsocket(fd, FIONBIO, &arg) == SOCKET_ERROR) + { + closeSocketNoThrow(fd); + SocketException ex(__FILE__, __LINE__); + ex.error = WSAGetLastError(); + throw ex; + } +#else + int flags = fcntl(fd, F_GETFL); + flags &= ~O_NONBLOCK; + if(fcntl(fd, F_SETFL, flags) == SOCKET_ERROR) + { + closeSocketNoThrow(fd); + SocketException ex(__FILE__, __LINE__); + ex.error = errno; + throw ex; + } +#endif + } + else + { +#ifdef _WIN32 + unsigned long arg = 1; + if(ioctlsocket(fd, FIONBIO, &arg) == SOCKET_ERROR) + { + closeSocketNoThrow(fd); + SocketException ex(__FILE__, __LINE__); + ex.error = WSAGetLastError(); + throw ex; + } +#else + int flags = fcntl(fd, F_GETFL); + flags |= O_NONBLOCK; + if(fcntl(fd, F_SETFL, flags) == SOCKET_ERROR) + { + closeSocketNoThrow(fd); + SocketException ex(__FILE__, __LINE__); + ex.error = errno; + throw ex; + } +#endif + } +} + +void +IceInternal::setTcpNoDelay(SOCKET fd) +{ + int flag = 1; + if(setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*)&flag, int(sizeof(int))) == SOCKET_ERROR) + { + closeSocketNoThrow(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) + { + closeSocketNoThrow(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) + { + closeSocketNoThrow(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 || + static_cast<unsigned int>(len) != sizeof(sz)) + { + closeSocketNoThrow(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) + { + closeSocketNoThrow(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 || + static_cast<unsigned int>(len) != sizeof(sz)) + { + closeSocketNoThrow(fd); + SocketException ex(__FILE__, __LINE__); + ex.error = getSocketErrno(); + throw ex; + } + return sz; +} + +void +IceInternal::setMcastGroup(SOCKET fd, const struct sockaddr_storage& group, const string& interface) +{ + int rc; + if(group.ss_family == AF_INET) + { + struct ip_mreq mreq; + mreq.imr_multiaddr = reinterpret_cast<const struct sockaddr_in*>(&group)->sin_addr; + mreq.imr_interface.s_addr = INADDR_ANY; + if(interface.size() > 0) + { + // + // First see if it is the interface name. If not check if IP Address. + // + mreq.imr_interface = getInterfaceAddress(interface); + if(mreq.imr_interface.s_addr == INADDR_ANY) + { + struct sockaddr_storage addr = getAddressForServer(interface, 0, EnableIPv4); + mreq.imr_interface = reinterpret_cast<const struct sockaddr_in*>(&addr)->sin_addr; + } + } + rc = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mreq, int(sizeof(mreq))); + } + else + { + struct ipv6_mreq mreq; + mreq.ipv6mr_multiaddr = reinterpret_cast<const struct sockaddr_in6*>(&group)->sin6_addr; + mreq.ipv6mr_interface = 0; + if(interface.size() != 0) + { + // + // First check if it is the interface name. If not check if index. + // + mreq.ipv6mr_interface = getInterfaceIndex(interface); + if(mreq.ipv6mr_interface == 0) + { + istringstream p(interface); + if(!(p >> mreq.ipv6mr_interface) || !p.eof()) + { + closeSocketNoThrow(fd); + SocketException ex(__FILE__, __LINE__); + ex.error = 0; + throw ex; + } + } + } + rc = setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char*)&mreq, int(sizeof(mreq))); + } + if(rc == SOCKET_ERROR) + { + closeSocketNoThrow(fd); + SocketException ex(__FILE__, __LINE__); + ex.error = getSocketErrno(); + throw ex; + } +} + +void +IceInternal::setMcastInterface(SOCKET fd, const string& interface, bool IPv4) +{ + int rc; + if(IPv4) + { + // + // First see if it is the interface name. If not check if IP Address. + // + struct in_addr iface = getInterfaceAddress(interface); + if(iface.s_addr == INADDR_ANY) + { + struct sockaddr_storage addr = getAddressForServer(interface, 0, EnableIPv4); + iface = reinterpret_cast<const struct sockaddr_in*>(&addr)->sin_addr; + } + rc = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, (char*)&iface, int(sizeof(iface))); + } + else + { + // + // First check if it is the interface name. If not check if index. + // + int interfaceNum = getInterfaceIndex(interface); + if(interfaceNum == 0) + { + istringstream p(interface); + if(!(p >> interfaceNum) || !p.eof()) + { + closeSocketNoThrow(fd); + SocketException ex(__FILE__, __LINE__); + ex.error = 0; + } + } + rc = setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, (char*)&interfaceNum, int(sizeof(int))); + } + if(rc == SOCKET_ERROR) + { + closeSocketNoThrow(fd); + SocketException ex(__FILE__, __LINE__); + ex.error = getSocketErrno(); + throw ex; + } +} + +void +IceInternal::setMcastTtl(SOCKET fd, int ttl, bool IPv4) +{ + int rc; + if(IPv4) + { + rc = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (char*)&ttl, int(sizeof(int))); + } + else + { + rc = setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char*)&ttl, int(sizeof(int))); + } + if(rc == SOCKET_ERROR) + { + closeSocketNoThrow(fd); + SocketException ex(__FILE__, __LINE__); + ex.error = getSocketErrno(); + throw ex; + } +} + +void +IceInternal::setReuseAddress(SOCKET fd, bool reuse) +{ + int flag = reuse ? 1 : 0; + if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&flag, int(sizeof(int))) == SOCKET_ERROR) + { + closeSocketNoThrow(fd); + SocketException ex(__FILE__, __LINE__); + ex.error = getSocketErrno(); + throw ex; + } +} + +struct sockaddr_storage +IceInternal::doBind(SOCKET fd, const struct sockaddr_storage& addr) +{ + int size; + if(addr.ss_family == AF_INET) + { + size = sizeof(sockaddr_in); + } + else if(addr.ss_family == AF_INET6) + { + size = sizeof(sockaddr_in6); + } + else + { + assert(false); + size = 0; // Keep the compiler happy. + } + + if(bind(fd, reinterpret_cast<const struct sockaddr*>(&addr), size) == SOCKET_ERROR) + { + closeSocketNoThrow(fd); + SocketException ex(__FILE__, __LINE__); + ex.error = getSocketErrno(); + throw ex; + } + + struct sockaddr_storage local; + socklen_t len = static_cast<socklen_t>(sizeof(local)); +#ifdef NDEBUG + getsockname(fd, reinterpret_cast<struct sockaddr*>(&local), &len); +#else + int ret = getsockname(fd, reinterpret_cast<struct sockaddr*>(&local), &len); + assert(ret != SOCKET_ERROR); +#endif + return local; +} + +void +IceInternal::doListen(SOCKET fd, int backlog) +{ +repeatListen: + if(::listen(fd, backlog) == SOCKET_ERROR) + { + if(interrupted()) + { + goto repeatListen; + } + + closeSocketNoThrow(fd); + SocketException ex(__FILE__, __LINE__); + ex.error = getSocketErrno(); + throw ex; + } +} + +bool +IceInternal::doConnect(SOCKET fd, const struct sockaddr_storage& addr) +{ +repeatConnect: + int size; + if(addr.ss_family == AF_INET) + { + size = sizeof(sockaddr_in); + } + else if(addr.ss_family == AF_INET6) + { + size = sizeof(sockaddr_in6); + } + else + { + assert(false); + size = 0; // Keep the compiler happy. + } + + if(::connect(fd, reinterpret_cast<const struct sockaddr*>(&addr), size) == SOCKET_ERROR) + { + if(interrupted()) + { + goto repeatConnect; + } + + if(connectInProgress()) + { + return false; + } + + closeSocketNoThrow(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; + } + } + +#if defined(__linux) + // + // Prevent self connect (self connect happens on Linux when a client tries to connect to + // a server which was just deactivated if the client socket re-uses the same ephemeral + // port as the server). + // + struct sockaddr_storage localAddr; + fdToLocalAddress(fd, localAddr); + if(compareAddress(addr, localAddr) == 0) + { + ConnectionRefusedException ex(__FILE__, __LINE__); + ex.error = 0; // No appropriate errno + throw ex; + } +#endif + return true; +} + +void +IceInternal::doFinishConnect(SOCKET fd) +{ + // + // Note: we don't close the socket if there's an exception. It's the responsability + // of the caller to do so. + // + + // + // Strange windows bug: The following call to Sleep() is + // necessary, otherwise no error is reported through + // getsockopt. + // +#ifdef _WIN32 + Sleep(0); +#endif + + int val; + socklen_t len = static_cast<socklen_t>(sizeof(int)); + if(getsockopt(fd, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&val), &len) == SOCKET_ERROR) + { + SocketException ex(__FILE__, __LINE__); + ex.error = getSocketErrno(); + throw ex; + } + + if(val > 0) + { +#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; + } + } + +#if defined(__linux) + // + // Prevent self connect (self connect happens on Linux when a client tries to connect to + // a server which was just deactivated if the client socket re-uses the same ephemeral + // port as the server). + // + struct sockaddr_storage localAddr; + fdToLocalAddress(fd, localAddr); + struct sockaddr_storage remoteAddr; + if(fdToRemoteAddress(fd, remoteAddr) && compareAddress(remoteAddr, localAddr) == 0) + { + ConnectionRefusedException ex(__FILE__, __LINE__); + ex.error = 0; // No appropriate errno + throw ex; + } +#endif +} + +#ifdef ICE_USE_IOCP +void +IceInternal::doConnectAsync(SOCKET fd, const struct sockaddr_storage& addr, AsyncInfo& info) +{ + // + // NOTE: It's the caller's responsability to close the socket upon + // failure to connect. The socket isn't closed by this method. + // + + struct sockaddr_storage bindAddr; + memset(&bindAddr, 0, sizeof(bindAddr)); + + int size; + if(addr.ss_family == AF_INET) + { + size = sizeof(sockaddr_in); + + struct sockaddr_in* addrin = reinterpret_cast<struct sockaddr_in*>(&bindAddr); + addrin->sin_family = AF_INET; + addrin->sin_port = htons(0); + addrin->sin_addr.s_addr = htonl(INADDR_ANY); + } + else if(addr.ss_family == AF_INET6) + { + size = sizeof(sockaddr_in6); + + struct sockaddr_in6* addrin = reinterpret_cast<struct sockaddr_in6*>(&bindAddr); + addrin->sin6_family = AF_INET6; + addrin->sin6_port = htons(0); + addrin->sin6_addr = in6addr_any; + } + else + { + assert(false); + size = 0; // Keep the compiler happy. + } + + if(bind(fd, reinterpret_cast<const struct sockaddr*>(&bindAddr), size) == SOCKET_ERROR) + { + SocketException ex(__FILE__, __LINE__); + ex.error = getSocketErrno(); + throw ex; + } + + LPFN_CONNECTEX ConnectEx = NULL; // a pointer to the 'ConnectEx()' function + GUID GuidConnectEx = WSAID_CONNECTEX; // The Guid + DWORD dwBytes; + if(WSAIoctl(fd, + SIO_GET_EXTENSION_FUNCTION_POINTER, + &GuidConnectEx, + sizeof(GuidConnectEx), + &ConnectEx, + sizeof(ConnectEx), + &dwBytes, + NULL, + NULL) == SOCKET_ERROR) + { + SocketException ex(__FILE__, __LINE__); + ex.error = getSocketErrno(); + throw ex; + } + + if(!ConnectEx(fd, reinterpret_cast<const struct sockaddr*>(&addr), size, 0, 0, 0, +#if defined(_MSC_VER) && (_MSC_VER < 1300) // COMPILER FIX: VC60 + reinterpret_cast<LPOVERLAPPED>(&info) +#else + &info +#endif + )) + { + if(!connectInProgress()) + { + 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; + } + } + } + +} + +void +IceInternal::doFinishConnectAsync(SOCKET fd, AsyncInfo& info) +{ + // + // NOTE: It's the caller's responsability to close the socket upon + // failure to connect. The socket isn't closed by this method. + // + + if(info.count == SOCKET_ERROR) + { + WSASetLastError(info.error); + 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; + } + } + + if(setsockopt(fd, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, NULL, 0) == SOCKET_ERROR) + { + SocketException ex(__FILE__, __LINE__); + ex.error = getSocketErrno(); + throw ex; + } +} +#endif + +SOCKET +IceInternal::doAccept(SOCKET fd) +{ +#ifdef _WIN32 + SOCKET ret; +#else + int ret; +#endif + +repeatAccept: + if((ret = ::accept(fd, 0, 0)) == INVALID_SOCKET) + { + if(acceptInterrupted()) + { + goto repeatAccept; + } + + SocketException ex(__FILE__, __LINE__); + ex.error = getSocketErrno(); + throw ex; + } + + setTcpNoDelay(ret); + setKeepAlive(ret); + return ret; +} + +struct sockaddr_storage +IceInternal::getAddressForServer(const string& host, int port, ProtocolSupport protocol) +{ + return getAddressImpl(host, port, protocol, true); +} + +struct sockaddr_storage +IceInternal::getAddress(const string& host, int port, ProtocolSupport protocol) +{ + return getAddressImpl(host, port, protocol, false); +} + +vector<struct sockaddr_storage> +IceInternal::getAddresses(const string& host, int port, ProtocolSupport protocol, bool blocking) +{ + vector<struct sockaddr_storage> result; + struct sockaddr_storage addr; + memset(&addr, 0, sizeof(struct sockaddr_storage)); + + // + // We don't use getaddrinfo when host is empty as it's not portable (some old Linux + // versions don't support it). + // + if(host.empty()) + { + if(protocol != EnableIPv4) + { + sockaddr_in6* addrin6 = reinterpret_cast<sockaddr_in6*>(&addr); + addrin6->sin6_family = AF_INET6; + addrin6->sin6_port = htons(port); + addrin6->sin6_addr = in6addr_loopback; + result.push_back(addr); + } + if(protocol != EnableIPv6) + { + sockaddr_in* addrin = reinterpret_cast<sockaddr_in*>(&addr); + addrin->sin_family = AF_INET; + addrin->sin_port = htons(port); + addrin->sin_addr.s_addr = htonl(INADDR_LOOPBACK); + result.push_back(addr); + } + return result; + } + + struct addrinfo* info = 0; + int retry = 5; + + struct addrinfo hints = { 0 }; + if(protocol == EnableIPv4) + { + hints.ai_family = PF_INET; + } + else if(protocol == EnableIPv6) + { + hints.ai_family = PF_INET6; + } + else + { + hints.ai_family = PF_UNSPEC; + } + + if(!blocking) + { + hints.ai_flags = AI_NUMERICHOST; + } + + int rs = 0; + do + { + rs = getaddrinfo(host.c_str(), 0, &hints, &info); + } + while(info == 0 && rs == EAI_AGAIN && --retry >= 0); + + // In theory, getaddrinfo should only return EAI_NONAME if AI_NUMERICHOST is specified and the host name + // is not a IP address. However on some platforms (e.g. Mac OS X 10.4.x) EAI_NODATA is also returned so + // we also check for it. +#ifdef EAI_NODATA + if(!blocking && (rs == EAI_NONAME || rs == EAI_NODATA)) +#else + if(!blocking && rs == EAI_NONAME) +#endif + { + return result; // Empty result indicates that a blocking lookup is necessary. + } + else if(rs != 0) + { + DNSException ex(__FILE__, __LINE__); + ex.error = rs; + ex.host = host; + throw ex; + } + + struct addrinfo* p; + for(p = info; p != NULL; p = p->ai_next) + { + memcpy(&addr, p->ai_addr, p->ai_addrlen); + if(p->ai_family == PF_INET) + { + struct sockaddr_in* addrin = reinterpret_cast<sockaddr_in*>(&addr); + addrin->sin_port = htons(port); + } + else if(p->ai_family == PF_INET6) + { + struct sockaddr_in6* addrin6 = reinterpret_cast<sockaddr_in6*>(&addr); + addrin6->sin6_port = htons(port); + } + + bool found = false; + for(unsigned int i = 0; i < result.size(); ++i) + { + if(compareAddress(result[i], addr) == 0) + { + found = true; + break; + } + } + if(!found) + { + result.push_back(addr); + } + } + + freeaddrinfo(info); + + if(result.size() == 0) + { + DNSException ex(__FILE__, __LINE__); + ex.host = host; + throw ex; + } + + return result; +} + +int +IceInternal::compareAddress(const struct sockaddr_storage& addr1, const struct sockaddr_storage& addr2) +{ + if(addr1.ss_family < addr2.ss_family) + { + return -1; + } + else if(addr2.ss_family < addr1.ss_family) + { + return 1; + } + + if(addr1.ss_family == AF_INET) + { + const struct sockaddr_in* addr1in = reinterpret_cast<const sockaddr_in*>(&addr1); + const struct sockaddr_in* addr2in = reinterpret_cast<const sockaddr_in*>(&addr2); + + if(addr1in->sin_port < addr2in->sin_port) + { + return -1; + } + else if(addr2in->sin_port < addr1in->sin_port) + { + return 1; + } + + if(addr1in->sin_addr.s_addr < addr2in->sin_addr.s_addr) + { + return -1; + } + else if(addr2in->sin_addr.s_addr < addr1in->sin_addr.s_addr) + { + return 1; + } + } + else + { + const struct sockaddr_in6* addr1in = reinterpret_cast<const sockaddr_in6*>(&addr1); + const struct sockaddr_in6* addr2in = reinterpret_cast<const sockaddr_in6*>(&addr2); + + if(addr1in->sin6_port < addr2in->sin6_port) + { + return -1; + } + else if(addr2in->sin6_port < addr1in->sin6_port) + { + return 1; + } + + int res = memcmp(&addr1in->sin6_addr, &addr2in->sin6_addr, sizeof(struct in6_addr)); + if(res < 0) + { + return -1; + } + else if(res > 0) + { + return 1; + } + } + + return 0; +} + +void +IceInternal::createPipe(SOCKET fds[2]) +{ +#ifdef _WIN32 + + SOCKET fd = createSocket(false, AF_INET); + setBlock(fd, true); + + struct sockaddr_storage addr; + memset(&addr, 0, sizeof(addr)); + + struct sockaddr_in* addrin = reinterpret_cast<struct sockaddr_in*>(&addr); + addrin->sin_family = AF_INET; + addrin->sin_port = htons(0); + addrin->sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + addr = doBind(fd, addr); + doListen(fd, 1); + + try + { + fds[0] = createSocket(false, AF_INET); + } + catch(...) + { + ::closesocket(fd); + throw; + } + + try + { + setBlock(fds[0], true); +#ifndef NDEBUG + bool connected = doConnect(fds[0], addr); + assert(connected); +#else + doConnect(fds[0], addr); +#endif + } + catch(...) + { + // fds[0] is closed by doConnect + ::closesocket(fd); + throw; + } + + try + { + fds[1] = doAccept(fd); + } + catch(...) + { + ::closesocket(fds[0]); + ::closesocket(fd); + throw; + } + + ::closesocket(fd); + + try + { + setBlock(fds[1], true); + } + catch(...) + { + ::closesocket(fds[0]); + // fds[1] is closed by setBlock + throw; + } + +#else + + if(::pipe(fds) != 0) + { + SyscallException ex(__FILE__, __LINE__); + ex.error = getSystemErrno(); + throw ex; + } + + try + { + setBlock(fds[0], true); + } + catch(...) + { + // fds[0] is closed by setBlock + closeSocketNoThrow(fds[1]); + throw; + } + + try + { + setBlock(fds[1], true); + } + catch(...) + { + closeSocketNoThrow(fds[0]); + // fds[1] is closed by setBlock + throw; + } + +#endif +} + +#ifdef _WIN32 + +string +IceInternal::errorToStringDNS(int error) +{ + return IceUtilInternal::errorToString(error); +} + +#else + +string +IceInternal::errorToStringDNS(int error) +{ + return gai_strerror(error); +} + +#endif + +std::string +IceInternal::fdToString(SOCKET fd) +{ + if(fd == INVALID_SOCKET) + { + return "<closed>"; + } + + struct sockaddr_storage localAddr; + fdToLocalAddress(fd, localAddr); + + struct sockaddr_storage remoteAddr; + bool peerConnected = fdToRemoteAddress(fd, remoteAddr); + + return addressesToString(localAddr, remoteAddr, peerConnected); +}; + +void +IceInternal::fdToAddressAndPort(SOCKET fd, string& localAddress, int& localPort, string& remoteAddress, int& remotePort) +{ + if(fd == INVALID_SOCKET) + { + localAddress.clear(); + remoteAddress.clear(); + localPort = -1; + remotePort = -1; + return; + } + + struct sockaddr_storage localAddr; + fdToLocalAddress(fd, localAddr); + addrToAddressAndPort(localAddr, localAddress, localPort); + + struct sockaddr_storage remoteAddr; + if(fdToRemoteAddress(fd, remoteAddr)) + { + addrToAddressAndPort(remoteAddr, remoteAddress, remotePort); + } + else + { + remoteAddress.clear(); + remotePort = -1; + } +} + +void +IceInternal::addrToAddressAndPort(const struct sockaddr_storage& addr, string& address, int& port) +{ + address = inetAddrToString(addr); + port = getPort(addr); +} + +std::string +IceInternal::addressesToString(const struct sockaddr_storage& localAddr, const struct sockaddr_storage& remoteAddr, + bool peerConnected) +{ + ostringstream s; + s << "local address = " << addrToString(localAddr); + if(peerConnected) + { + s << "\nremote address = " << addrToString(remoteAddr); + } + else + { + s << "\nremote address = <not connected>"; + } + return s.str(); +} + +void +IceInternal::fdToLocalAddress(SOCKET fd, struct sockaddr_storage& addr) +{ + socklen_t len = static_cast<socklen_t>(sizeof(struct sockaddr_storage)); + if(getsockname(fd, reinterpret_cast<struct sockaddr*>(&addr), &len) == SOCKET_ERROR) + { + closeSocketNoThrow(fd); + SocketException ex(__FILE__, __LINE__); + ex.error = getSocketErrno(); + throw ex; + } +} + +bool +IceInternal::fdToRemoteAddress(SOCKET fd, struct sockaddr_storage& addr) +{ + socklen_t len = static_cast<socklen_t>(sizeof(struct sockaddr_storage)); + if(getpeername(fd, reinterpret_cast<struct sockaddr*>(&addr), &len) == SOCKET_ERROR) + { + if(notConnected()) + { + return false; + } + else + { + closeSocketNoThrow(fd); + SocketException ex(__FILE__, __LINE__); + ex.error = getSocketErrno(); + throw ex; + } + } + + return true; +} + +string +IceInternal::inetAddrToString(const struct sockaddr_storage& ss) +{ + int size = 0; + if(ss.ss_family == AF_INET) + { + size = sizeof(sockaddr_in); + } + else if(ss.ss_family == AF_INET6) + { + size = sizeof(sockaddr_in6); + } + else + { + return ""; + } + + char namebuf[1024]; + namebuf[0] = '\0'; + getnameinfo(reinterpret_cast<const struct sockaddr *>(&ss), size, namebuf, sizeof(namebuf), 0, 0, NI_NUMERICHOST); + return string(namebuf); +} + +string +IceInternal::addrToString(const struct sockaddr_storage& addr) +{ + ostringstream s; + string port; + s << inetAddrToString(addr) << ':' << getPort(addr); + return s.str(); +} + +bool +IceInternal::isMulticast(const struct sockaddr_storage& addr) +{ + if(addr.ss_family == AF_INET) + { + return IN_MULTICAST(ntohl(reinterpret_cast<const struct sockaddr_in*>(&addr)->sin_addr.s_addr)); + } + else if(addr.ss_family == AF_INET6) + { + return IN6_IS_ADDR_MULTICAST(&reinterpret_cast<const struct sockaddr_in6*>(&addr)->sin6_addr); + } + else + { + return false; + } +} + +int +IceInternal::getPort(const struct sockaddr_storage& addr) +{ + if(addr.ss_family == AF_INET) + { + return ntohs(reinterpret_cast<const sockaddr_in*>(&addr)->sin_port); + } + else if(addr.ss_family == AF_INET6) + { + return ntohs(reinterpret_cast<const sockaddr_in6*>(&addr)->sin6_port); + } + else + { + return -1; + } +} + +void +IceInternal::setPort(struct sockaddr_storage& addr, int port) +{ + if(addr.ss_family == AF_INET) + { + reinterpret_cast<sockaddr_in*>(&addr)->sin_port = htons(port); + } + else + { + assert(addr.ss_family == AF_INET6); + reinterpret_cast<sockaddr_in6*>(&addr)->sin6_port = htons(port); + } +} + +vector<string> +IceInternal::getHostsForEndpointExpand(const string& host, ProtocolSupport protocolSupport, bool includeLoopback) +{ + vector<string> hosts; + if(host.empty() || isWildcard(host, protocolSupport)) + { + vector<struct sockaddr_storage> addrs = getLocalAddresses(protocolSupport); + for(vector<struct sockaddr_storage>::const_iterator p = addrs.begin(); p != addrs.end(); ++p) + { + // + // NOTE: We don't publish link-local IPv6 addresses as these addresses can only + // be accessed in general with a scope-id. + // + if(p->ss_family != AF_INET6 || + !IN6_IS_ADDR_LINKLOCAL(&reinterpret_cast<const struct sockaddr_in6*>(&(*p))->sin6_addr)) + { + hosts.push_back(inetAddrToString(*p)); + } + } + + if(hosts.empty() || includeLoopback) + { + if(protocolSupport != EnableIPv6) + { + hosts.push_back("127.0.0.1"); + } + if(protocolSupport != EnableIPv4) + { + hosts.push_back("0:0:0:0:0:0:0:1"); + } + } + } + return hosts; // An empty host list indicates to just use the given host. +} + +void +IceInternal::setTcpBufSize(SOCKET fd, const Ice::PropertiesPtr& properties, const Ice::LoggerPtr& logger) +{ + assert(fd != INVALID_SOCKET); + + // + // By default, on Windows we use a 128KB buffer size. On Unix + // platforms, we use the system defaults. + // +#ifdef _WIN32 + const int dfltBufSize = 128 * 1024; +#else + const int dfltBufSize = 0; +#endif + Int sizeRequested; + + sizeRequested = properties->getPropertyAsIntWithDefault("Ice.TCP.RcvSize", dfltBufSize); + if(sizeRequested > 0) + { + // + // Try to set the buffer size. The kernel will silently adjust + // the size to an acceptable value. Then read the size back to + // get the size that was actually set. + // + setRecvBufferSize(fd, sizeRequested); + int size = getRecvBufferSize(fd); + if(size < sizeRequested) // Warn if the size that was set is less than the requested size. + { + Ice::Warning out(logger); + out << "TCP receive buffer size: requested size of " << sizeRequested << " adjusted to " << size; + } + } + + sizeRequested = properties->getPropertyAsIntWithDefault("Ice.TCP.SndSize", dfltBufSize); + if(sizeRequested > 0) + { + // + // Try to set the buffer size. The kernel will silently adjust + // the size to an acceptable value. Then read the size back to + // get the size that was actually set. + // + setSendBufferSize(fd, sizeRequested); + int size = getSendBufferSize(fd); + if(size < sizeRequested) // Warn if the size that was set is less than the requested size. + { + Ice::Warning out(logger); + out << "TCP send buffer size: requested size of " << sizeRequested << " adjusted to " << size; + } + } +} |