#include "VoidNet/Socket.hpp" #include "VoidNet/IPAddress.hpp" namespace std::net { void Socket::init() { if (GetSocketType() == SocketType::Unknown) throw std::invalid_argument("Unknown socket type"); if (m_socket == INVALID_SOCKET) { m_socket = socket(AF_INET, (int)GetSocketType(), 0); if (m_socket == INVALID_SOCKET) throw std::runtime_error("Couldnt create socket"); } if (GetSocketType() == SocketType::Streaming) { int yes = 1; // Disable the Nagle algorithm (i.e. removes buffering of TCP packets) setsockopt(m_socket, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast(&yes), sizeof(yes)); // On Mac OS X, disable the SIGPIPE signal on disconnection #if defined(__APPLE__) && defined(__MACH__) setsockopt(m_socket, SOL_SOCKET, SO_NOSIGPIPE, reinterpret_cast(&yes), sizeof(yes)); #endif } else { // Enable broadcast by default for UDP sockets int yes = 1; setsockopt(m_socket, SOL_SOCKET, SO_BROADCAST, reinterpret_cast(&yes), sizeof(yes)); } } bool Socket::Close() { if (m_socket != INVALID_SOCKET) { int32_t error = closesocket(m_socket); m_socket = INVALID_SOCKET; return error == 0; } return false; } bool Socket::Bind(const IPAddress &addr) { sockaddr_in addr_in = addr.ToCAddr(); return bind(m_socket, (sockaddr*)&addr_in, sizeof(sockaddr_in)) == 0; } bool Socket::Connect(const IPAddress& addr) { sockaddr_in addr_in = addr.ToCAddr(); int32_t Return = connect(m_socket, (sockaddr*)&addr_in, sizeof(sockaddr_in)); SocketErrors Error = TranslateErrorCode(Return); // "would block" is not an error return ((Error == SocketErrors::SE_NO_ERROR) || (Error == SocketErrors::SE_EWOULDBLOCK)); } bool Socket::WaitForPendingConnection(bool& hasPendingConnection, std::chrono::milliseconds t) { bool hasSucceeded = false; hasPendingConnection = false; if (HasState(SocketParam::HasError) == SocketReturn::No) { SocketReturn state = HasState(SocketParam::CanRead, t); hasSucceeded = state != SocketReturn::EncounteredError; hasPendingConnection = state == SocketReturn::Yes; } return hasSucceeded; } bool Socket::HasPendingData(uint32_t& pendingDataSize) { pendingDataSize = 0; if (HasState(SocketParam::CanRead) == SocketReturn::Yes) { if (ioctl(m_socket, FIONREAD, #if defined(_WIN32) (u_long*) #endif &pendingDataSize) == 0) return (pendingDataSize > 0); } return false; } std::unique_ptr Socket::Accept() { SOCKET newSocket = accept(m_socket, nullptr, nullptr); if (newSocket != INVALID_SOCKET) { return std::make_unique(newSocket, GetSocketType()); } return nullptr; } bool Socket::SendTo(const uint8_t* data, int32_t count, int32_t& sent, const IPAddress& addrDest) { sockaddr_in addr = addrDest.ToCAddr(); sent = sendto(m_socket, (const char*)data, count, 0, (sockaddr*)&addr, sizeof(sockaddr_in)); bool result = sent >= 0; if (result) m_lastActivityTime = std::chrono::system_clock::now().time_since_epoch().count(); return result; } bool Socket::Send(const uint8_t* data, int32_t count, int32_t& sent) { sent = send(m_socket, (const char*)data, count, 0); bool result = sent != SOCKET_ERROR; if (result) m_lastActivityTime = std::chrono::system_clock::now().time_since_epoch().count(); return result; } bool Socket::RecvFrom(uint8_t* data, int32_t size, int32_t& read, IPAddress& srcAddr, SocketReceiveFlags flags) { socklen_t len = sizeof(sockaddr_in); sockaddr_in addr = srcAddr.ToCAddr(); const int translatedFlags = TranslateFlags(flags); read = recvfrom(m_socket, (char*)data, size, translatedFlags, (sockaddr*)&addr, &len); if (read < 0 && TranslateErrorCode(read) == SocketErrors::SE_EWOULDBLOCK) read = 0; else if (read <= 0) // 0 means gracefully closed { read = 0; return false; } m_lastActivityTime = std::chrono::system_clock::now().time_since_epoch().count(); return true; } bool Socket::Recv(uint8_t* data, int32_t size, int32_t& read, SocketReceiveFlags flags) { const int translatedFlags = TranslateFlags(flags); read = recv(m_socket, (char*)data, size, translatedFlags); if (read < 0 && TranslateErrorCode(read) == SocketErrors::SE_EWOULDBLOCK) read = 0; else if (read <= 0) // 0 means gracefully closed { read = 0; return false; } m_lastActivityTime = std::chrono::system_clock::now().time_since_epoch().count(); return true; } bool Socket::Wait(SocketWaitConditions cond, std::chrono::milliseconds t) { if ((cond == SocketWaitConditions::WaitForRead) || (cond == SocketWaitConditions::WaitForReadOrWrite)) { if (HasState(SocketParam::CanRead, t) == SocketReturn::Yes) return true; } if ((cond == SocketWaitConditions::WaitForWrite) || (cond == SocketWaitConditions::WaitForReadOrWrite)) { if (HasState(SocketParam::CanWrite, t) == SocketReturn::Yes) return true; } return false; } SocketConnectionState Socket::GetConnectionState() { SocketConnectionState currentState = SocketConnectionState::ConnectionError; if (HasState(SocketParam::HasError) == SocketReturn::No) { if (std::chrono::system_clock::now().time_since_epoch().count() - m_lastActivityTime > std::chrono::milliseconds(5).count()) { SocketReturn writeState = HasState(SocketParam::CanWrite); SocketReturn readState = HasState(SocketParam::CanRead); if (writeState == SocketReturn::Yes || readState == SocketReturn::Yes) { currentState = SocketConnectionState::Connected; m_lastActivityTime = std::chrono::system_clock::now().time_since_epoch().count(); } else if (writeState == SocketReturn::No && readState == SocketReturn::No) currentState = SocketConnectionState::NotConnected; } else currentState = SocketConnectionState::Connected; } return currentState; } void Socket::GetAddress(IPAddress& outAddr) { struct sockaddr_in addr; socklen_t Size = sizeof(sockaddr_in); if (getsockname(m_socket, (sockaddr*)&addr, &Size) != 0) return; outAddr = IPAddress(inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); } bool Socket::GetPeerAddress(IPAddress& outAddr) { struct sockaddr_in addr; socklen_t size = sizeof(sockaddr_in); int result = getpeername(m_socket, (sockaddr*)&addr, &size); if (result != 0) return false; outAddr = IPAddress(inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); return result == 0; } bool Socket::SetNonBlocking(bool isNonBlocking) { #if PLATFORM_HTML5 // if we have more platforms later (html5, android, ios) later we need to do some changes to networking throw std::exception("Can't have blocking sockets on HTML5"); return false; #else #if _WIN32 return ioctl(m_socket, FIONBIO, (u_long*)&isNonBlocking) == 0; #else int flags = fcntl(m_socket, F_GETFL, 0); flags = isNonBlocking ? flags | O_NONBLOCK : flags ^ (flags & O_NONBLOCK); int err = fcntl(m_socket, F_SETFL, flags); return (err == 0 ? true : false); #endif #endif } bool Socket::JoinMulticastGroup(const IPAddress& addrStr) { sockaddr_in addr = addrStr.ToCAddr(); ip_mreq imr; imr.imr_interface.s_addr = INADDR_ANY; imr.imr_multiaddr = addr.sin_addr; return (setsockopt(m_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&imr, sizeof(imr)) == 0); } bool Socket::LeaveMulticastGroup(const IPAddress& addrStr) { sockaddr_in addr = addrStr.ToCAddr(); ip_mreq imr; imr.imr_interface.s_addr = INADDR_ANY; imr.imr_multiaddr = addr.sin_addr; return (setsockopt(m_socket, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char*)&imr, sizeof(imr)) == 0); } bool Socket::SetLinger(bool shouldLinger, int32_t t) { linger ling; ling.l_onoff = shouldLinger; ling.l_linger = t; return setsockopt(m_socket, SOL_SOCKET, SO_LINGER, (char*)&ling, sizeof(ling)) == 0; } bool Socket::SetSendBufferSize(int32_t size, int32_t& newSize) { socklen_t len = sizeof(int32_t); bool success = setsockopt(m_socket, SOL_SOCKET, SO_SNDBUF, (char*)&size, sizeof(int32_t)) == 0; getsockopt(m_socket, SOL_SOCKET, SO_SNDBUF, (char*)&newSize, &len); return success; } bool Socket::SetReceiveBufferSize(int32_t size, int32_t& newSize) { socklen_t len = sizeof(int32_t); bool success = setsockopt(m_socket, SOL_SOCKET, SO_RCVBUF, (char*)&size, sizeof(int32_t)) == 0; getsockopt(m_socket, SOL_SOCKET, SO_RCVBUF, (char*)&newSize, &len); return success; } uint32_t Socket::GetPort() { sockaddr_in addr; socklen_t size = sizeof(sockaddr_in); if (getsockname(m_socket, (sockaddr*)&addr, &size) != 0) throw std::runtime_error("Invalid port"); return ntohs(addr.sin_port); } SocketReturn Socket::HasState(SocketParam state, std::chrono::milliseconds t) { timeval time; time.tv_sec = t.count(); time.tv_usec = t.count() * 1000 + t.count(); fd_set socketSet; FD_ZERO(&socketSet); FD_SET(m_socket, &socketSet); int32_t SelectStatus = 0; switch (state) { case SocketParam::CanRead: SelectStatus = select(m_socket + 1, &socketSet, nullptr, nullptr, &time); break; case SocketParam::CanWrite: SelectStatus = select(m_socket + 1, nullptr, &socketSet, nullptr, &time); break; case SocketParam::HasError: SelectStatus = select(m_socket + 1, nullptr, nullptr, &socketSet, &time); break; } return SelectStatus > 0 ? SocketReturn::Yes : SelectStatus == 0 ? SocketReturn::No : SocketReturn::EncounteredError; } SocketErrors Socket::TranslateErrorCode(int32_t code) { #if !_WIN32 if (code == SOCKET_ERROR) { return SE_SOCKET_ERROR; } switch (code) { case 0: return SocketErrors::SE_NO_ERROR; case EINTR: return SocketErrors::SE_EINTR; case EBADF: return SocketErrors::SE_EBADF; case EACCES: return SocketErrors::SE_EACCES; case EFAULT: return SocketErrors::SE_EFAULT; case EINVAL: return SocketErrors::SE_EINVAL; case EMFILE: return SocketErrors::SE_EMFILE; case EWOULDBLOCK: return SocketErrors::SE_EWOULDBLOCK; case EINPROGRESS: return SocketErrors::SE_EINPROGRESS; case EALREADY: return SocketErrors::SE_EALREADY; case ENOTSOCK: return SocketErrors::SE_ENOTSOCK; case EDESTADDRREQ: return SocketErrors::SE_EDESTADDRREQ; case EMSGSIZE: return SocketErrors::SE_EMSGSIZE; case EPROTOTYPE: return SocketErrors::SE_EPROTOTYPE; case ENOPROTOOPT: return SocketErrors::SE_ENOPROTOOPT; case EPROTONOSUPPORT: return SocketErrors::SE_EPROTONOSUPPORT; case ESOCKTNOSUPPORT: return SocketErrors::SE_ESOCKTNOSUPPORT; case EOPNOTSUPP: return SocketErrors::SE_EOPNOTSUPP; case EPFNOSUPPORT: return SocketErrors::SE_EPFNOSUPPORT; case EAFNOSUPPORT: return SocketErrors::SE_EAFNOSUPPORT; case EADDRINUSE: return SocketErrors::SE_EADDRINUSE; case EADDRNOTAVAIL: return SocketErrors::SE_EADDRNOTAVAIL; case ENETDOWN: return SocketErrors::SE_ENETDOWN; case ENETUNREACH: return SocketErrors::SE_ENETUNREACH; case ENETRESET: return SocketErrors::SE_ENETRESET; case ECONNABORTED: return SocketErrors::SE_ECONNABORTED; case ECONNRESET: return SocketErrors::SE_ECONNRESET; case ENOBUFS: return SocketErrors::SE_ENOBUFS; case EISCONN: return SocketErrors::SE_EISCONN; case ENOTCONN: return SocketErrors::SE_ENOTCONN; case ESHUTDOWN: return SocketErrors::SE_ESHUTDOWN; case ETOOMANYREFS: return SocketErrors::SE_ETOOMANYREFS; case ETIMEDOUT: return SocketErrors::SE_ETIMEDOUT; case ECONNREFUSED: return SocketErrors::SE_ECONNREFUSED; case ELOOP: return SocketErrors::SE_ELOOP; case ENAMETOOLONG: return SocketErrors::SE_ENAMETOOLONG; case EHOSTDOWN: return SocketErrors::SE_EHOSTDOWN; case EHOSTUNREACH: return SocketErrors::SE_EHOSTUNREACH; case ENOTEMPTY: return SocketErrors::SE_ENOTEMPTY; case EUSERS: return SocketErrors::SE_EUSERS; case EDQUOT: return SocketErrors::SE_EDQUOT; case ESTALE: return SocketErrors::SE_ESTALE; case EREMOTE: return SocketErrors::SE_EREMOTE; case ENODEV: return SocketErrors::SE_NODEV; #if !PLATFORM_HAS_NO_EPROCLIM case EPROCLIM: return SocketErrors::SE_EPROCLIM; #endif // case EDISCON: return SE_EDISCON; // case SYSNOTREADY: return SE_SYSNOTREADY; // case VERNOTSUPPORTED: return SE_VERNOTSUPPORTED; // case NOTINITIALISED: return SE_NOTINITIALISED; #if PLATFORM_HAS_BSD_SOCKET_FEATURE_GETHOSTNAME case HOST_NOT_FOUND: return SocketErrors::SE_HOST_NOT_FOUND; case TRY_AGAIN: return SocketErrors::SE_TRY_AGAIN; case NO_RECOVERY: return SocketErrors::SE_NO_RECOVERY; #endif // case NO_DATA: return SE_NO_DATA; // case : return SE_UDP_ERR_PORT_UNREACH; //@TODO Find it's replacement } return SocketErrors::SE_EINVAL; #else // handle the generic -1 error if (code == SOCKET_ERROR) { return SocketErrors::SE_SOCKET_ERROR; } switch (code) { case 0: return SocketErrors::SE_NO_ERROR; case ERROR_INVALID_HANDLE: return SocketErrors::SE_ECONNRESET; // invalid socket handle catch case WSAEINTR: return SocketErrors::SE_EINTR; case WSAEBADF: return SocketErrors::SE_EBADF; case WSAEACCES: return SocketErrors::SE_EACCES; case WSAEFAULT: return SocketErrors::SE_EFAULT; case WSAEINVAL: return SocketErrors::SE_EINVAL; case WSAEMFILE: return SocketErrors::SE_EMFILE; case WSAEWOULDBLOCK: return SocketErrors::SE_EWOULDBLOCK; case WSAEINPROGRESS: return SocketErrors::SE_EINPROGRESS; case WSAEALREADY: return SocketErrors::SE_EALREADY; case WSAENOTSOCK: return SocketErrors::SE_ENOTSOCK; case WSAEDESTADDRREQ: return SocketErrors::SE_EDESTADDRREQ; case WSAEMSGSIZE: return SocketErrors::SE_EMSGSIZE; case WSAEPROTOTYPE: return SocketErrors::SE_EPROTOTYPE; case WSAENOPROTOOPT: return SocketErrors::SE_ENOPROTOOPT; case WSAEPROTONOSUPPORT: return SocketErrors::SE_EPROTONOSUPPORT; case WSAESOCKTNOSUPPORT: return SocketErrors::SE_ESOCKTNOSUPPORT; case WSAEOPNOTSUPP: return SocketErrors::SE_EOPNOTSUPP; case WSAEPFNOSUPPORT: return SocketErrors::SE_EPFNOSUPPORT; case WSAEAFNOSUPPORT: return SocketErrors::SE_EAFNOSUPPORT; case WSAEADDRINUSE: return SocketErrors::SE_EADDRINUSE; case WSAEADDRNOTAVAIL: return SocketErrors::SE_EADDRNOTAVAIL; case WSAENETDOWN: return SocketErrors::SE_ENETDOWN; case WSAENETUNREACH: return SocketErrors::SE_ENETUNREACH; case WSAENETRESET: return SocketErrors::SE_ENETRESET; case WSAECONNABORTED: return SocketErrors::SE_ECONNABORTED; case WSAECONNRESET: return SocketErrors::SE_ECONNRESET; case WSAENOBUFS: return SocketErrors::SE_ENOBUFS; case WSAEISCONN: return SocketErrors::SE_EISCONN; case WSAENOTCONN: return SocketErrors::SE_ENOTCONN; case WSAESHUTDOWN: return SocketErrors::SE_ESHUTDOWN; case WSAETOOMANYREFS: return SocketErrors::SE_ETOOMANYREFS; case WSAETIMEDOUT: return SocketErrors::SE_ETIMEDOUT; case WSAECONNREFUSED: return SocketErrors::SE_ECONNREFUSED; case WSAELOOP: return SocketErrors::SE_ELOOP; case WSAENAMETOOLONG: return SocketErrors::SE_ENAMETOOLONG; case WSAEHOSTDOWN: return SocketErrors::SE_EHOSTDOWN; case WSAEHOSTUNREACH: return SocketErrors::SE_EHOSTUNREACH; case WSAENOTEMPTY: return SocketErrors::SE_ENOTEMPTY; case WSAEPROCLIM: return SocketErrors::SE_EPROCLIM; case WSAEUSERS: return SocketErrors::SE_EUSERS; case WSAEDQUOT: return SocketErrors::SE_EDQUOT; case WSAESTALE: return SocketErrors::SE_ESTALE; case WSAEREMOTE: return SocketErrors::SE_EREMOTE; case WSAEDISCON: return SocketErrors::SE_EDISCON; case WSASYSNOTREADY: return SocketErrors::SE_SYSNOTREADY; case WSAVERNOTSUPPORTED: return SocketErrors::SE_VERNOTSUPPORTED; case WSANOTINITIALISED: return SocketErrors::SE_NOTINITIALISED; case WSAHOST_NOT_FOUND: return SocketErrors::SE_HOST_NOT_FOUND; case WSATRY_AGAIN: return SocketErrors::SE_TRY_AGAIN; case WSANO_RECOVERY: return SocketErrors::SE_NO_RECOVERY; case WSANO_DATA: return SocketErrors::SE_NO_DATA; // case : return SE_UDP_ERR_PORT_UNREACH; } return SocketErrors::SE_NO_ERROR; #endif } int Socket::TranslateFlags(SocketReceiveFlags flags) { int translatedFlags = 0; if ((int)flags & (int)SocketReceiveFlags::Peek) { translatedFlags |= MSG_PEEK; #if !_WIN32 translatedFlags |= MSG_DONTWAIT; #endif } if ((int)flags & (int)SocketReceiveFlags::WaitAll) { translatedFlags |= MSG_WAITALL; } return translatedFlags; } }