diff --git a/src/PassiveSocket.cpp b/src/PassiveSocket.cpp index 2d6a1f8..aca5877 100644 --- a/src/PassiveSocket.cpp +++ b/src/PassiveSocket.cpp @@ -80,21 +80,26 @@ bool CPassiveSocket::BindMulticast(const char *pInterface, const char *pGroup, u // If no IP Address (interface ethn) is supplied, or the loop back is // specified then bind to any interface, else bind to specified interface. //-------------------------------------------------------------------------- - if ((pInterface == NULL) || (!strlen(pInterface))) + if (pInterface && pInterface[0]) { - m_stMulticastGroup.sin_addr.s_addr = htonl(INADDR_ANY); + inet_pton(AF_INET, pInterface, &inAddr); } else { - if ((inAddr = inet_addr(pInterface)) != INADDR_NONE) - { - m_stMulticastGroup.sin_addr.s_addr = inAddr; - } + inAddr = INADDR_ANY; + } + + if (pGroup && pGroup[0] != 0) + { + m_stMulticastGroup.sin_addr.s_addr = htonl(INADDR_ANY); + } + else + { + m_stMulticastGroup.sin_addr.s_addr = inAddr; } // multicast address/port is the server memcpy(&m_stServerSockaddr,&m_stMulticastGroup,sizeof(m_stServerSockaddr)); - ClearSystemError(); //-------------------------------------------------------------------------- @@ -103,13 +108,13 @@ bool CPassiveSocket::BindMulticast(const char *pInterface, const char *pGroup, u m_bIsServerSide = true; if (bind(m_socket, (struct sockaddr *)&m_stMulticastGroup, sizeof(m_stMulticastGroup)) == 0) { - if ( pGroup ) + if ( pGroup && pGroup[0] != 0 ) { //---------------------------------------------------------------------- // Join the multicast group //---------------------------------------------------------------------- - m_stMulticastRequest.imr_multiaddr.s_addr = inet_addr(pGroup); - m_stMulticastRequest.imr_interface.s_addr = m_stMulticastGroup.sin_addr.s_addr; + inet_pton(AF_INET, pGroup, &m_stMulticastRequest.imr_multiaddr.s_addr); + m_stMulticastRequest.imr_interface.s_addr = inAddr; if ( SETSOCKOPT(m_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&m_stMulticastRequest, @@ -148,7 +153,9 @@ bool CPassiveSocket::BindMulticast(const char *pInterface, const char *pGroup, u //------------------------------------------------------------------------------ // -// Listen() - +// Listen() - Create a listening socket (server) at local ip address 'x.x.x.x' or 'localhost' +// waiting for an incoming connection from client(s) +// also see .h // //------------------------------------------------------------------------------ bool CPassiveSocket::Listen(const char *pAddr, uint16 nPort, int32 nConnectionBacklog) @@ -189,7 +196,8 @@ bool CPassiveSocket::Listen(const char *pAddr, uint16 nPort, int32 nConnectionBa } else { - if ((inAddr = inet_addr(pAddr)) != INADDR_NONE) + inet_pton(AF_INET, pAddr, &inAddr); + if (inAddr != INADDR_NONE) { m_stServerSockaddr.sin_addr.s_addr = inAddr; } @@ -268,7 +276,7 @@ CSimpleSocket *CPassiveSocket::Accept() errno = 0; socket = accept(m_socket, (struct sockaddr *)&m_stClientSockaddr, (socklen_t *)&nSockLen); - if (socket != -1) + if (socket != INVALID_SOCKET) { pClientSocket = new CSimpleSocket(); if ( !pClientSocket ) diff --git a/src/PassiveSocket.h b/src/PassiveSocket.h index e629c5b..e2cc062 100644 --- a/src/PassiveSocket.h +++ b/src/PassiveSocket.h @@ -97,7 +97,8 @@ class CPassiveSocket : public CSimpleSocket { return BindMulticast(pInterface, pNoGroup, nPort); } - /// Create a listening socket at local ip address 'x.x.x.x' or 'localhost' + /// Create a listening socket (server) at local ip address 'x.x.x.x' or 'localhost' + /// waiting for an incoming connection from client(s) /// if pAddr is NULL on port nPort. /// /// @param pAddr specifies the IP address on which to listen. diff --git a/src/SimpleSocket.cpp b/src/SimpleSocket.cpp index 293ff5b..227f2a1 100644 --- a/src/SimpleSocket.cpp +++ b/src/SimpleSocket.cpp @@ -46,9 +46,30 @@ #include #endif #include +#include + +#ifdef _MSC_VER +#pragma warning( disable : 4996 ) +#define snprintf _snprintf +#endif #define PRINT_CLOSE 0 #define PRINT_ERRORS 0 +#define CHECK_PRINT_RUNTIME_ERR 0 + + +#if CHECK_PRINT_RUNTIME_ERR + +#include + +static void CoreDump() +{ + int * ptr = nullptr; + while (1) + *ptr++ = -1; +} + +#endif CSimpleSocket::CSimpleSocket(CSocketType nType) : @@ -151,6 +172,155 @@ CSimpleSocket *CSimpleSocket::operator=(CSimpleSocket &socket) } +bool CSimpleSocket::GetAddrInfoStatic(const char *pAddr, uint16 nPort, struct in_addr * pOutIpAddress, CSocketType nSocketType ) +{ + char aServ[64]; + char * pServ = aServ; + struct addrinfo hints, *servinfo, *p; + int rv; + int nSocketDomain = 0; + + memset(&hints, 0, sizeof(hints)); + switch ( nSocketType ) + { + default: + case SocketTypeInvalid: +#ifdef _WIN32 + case SocketTypeRaw: +#endif + return false; + case SocketTypeTcp6: + hints.ai_family = nSocketDomain = AF_INET6; + hints.ai_socktype = SOCK_STREAM; + break; + case SocketTypeTcp: + hints.ai_family = nSocketDomain = AF_INET; + hints.ai_socktype = SOCK_STREAM; + break; + case SocketTypeUdp6: + hints.ai_family = nSocketDomain = AF_INET6; + hints.ai_socktype = SOCK_DGRAM; + break; + case SocketTypeUdp: + hints.ai_family = nSocketDomain = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + break; +#if defined(_LINUX) && !defined(_DARWIN) + case SocketTypeRaw: + hints.ai_family = nSocketDomain = AF_PACKET; + hints.ai_socktype = SOCK_RAW; + break; +#endif + } + + if ( nPort ) + snprintf( aServ, 63, "%d", (int)nPort ); + else + pServ = nullptr; + + rv = getaddrinfo(pAddr, pServ, &hints, &servinfo ); + if ( rv != 0 ) + return false; + + // loop through all the results + for ( p = servinfo; p != NULL; p = p->ai_next ) + { + // creation of socket already done in Initialize() + // check, that protocol and socktype fit + if ( p->ai_family != nSocketDomain || p->ai_socktype != nSocketType + || p->ai_addr->sa_family != nSocketDomain ) + { + continue; + } + + // struct sockaddr_in * pOutSin + //*pOutSin = *( (struct sockaddr_in *) p->ai_addr ); + + // struct in_addr * pOutIpAddress + *pOutIpAddress = ( (struct sockaddr_in *)p->ai_addr )->sin_addr; + + freeaddrinfo(servinfo); // all done with this structure + return true; + } + + freeaddrinfo(servinfo); // all done with this structure + return false; +} + + +bool CSimpleSocket::GetAddrInfo(const char *pAddr, uint16 nPort, struct in_addr * pOutIpAddress ) +{ + char aServ[64]; + char * pServ = aServ; + struct addrinfo hints, *servinfo, *p; + int rv; + + if (m_socket == INVALID_SOCKET) + { + return false; + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = m_nSocketDomain; // AF_INET for IPv4, AF_UNSPEC for any, AF_INET6 to force IPv6 + switch ( m_nSocketType ) + { + default: + case SocketTypeInvalid: + return false; + case SocketTypeTcp6: + case SocketTypeTcp: + hints.ai_socktype = SOCK_STREAM; + break; + case SocketTypeUdp6: + case SocketTypeUdp: + hints.ai_socktype = SOCK_DGRAM; + break; + case SocketTypeRaw: + hints.ai_socktype = SOCK_RAW; + break; + } + + if ( nPort ) + snprintf( aServ, 63, "%d", (int)nPort ); + else + pServ = nullptr; + + rv = getaddrinfo(pAddr, pServ, &hints, &servinfo ); + if ( rv != 0 ) + { + // gai_strerror(rv); + SetSocketError(CSimpleSocket::SocketInvalidAddress); + return false; + } + + // loop through all the results + for ( p = servinfo; p != NULL; p = p->ai_next ) + { + // creation of socket already done in Initialize() + // check, that protocol and socktype fit + if ( p->ai_family != m_nSocketDomain || p->ai_socktype != m_nSocketType + || p->ai_addr->sa_family != m_nSocketDomain ) + { + continue; + } + + // struct sockaddr_in * pOutSin + //*pOutSin = *( (struct sockaddr_in *) p->ai_addr ); + + // struct in_addr * pOutIpAddress + *pOutIpAddress = ( (struct sockaddr_in *)p->ai_addr )->sin_addr; + + freeaddrinfo(servinfo); // all done with this structure + return true; + } + + freeaddrinfo(servinfo); // all done with this structure + + SetSocketError(CSimpleSocket::SocketInvalidAddress); + return false; +} + + //------------------------------------------------------------------------------ // // ConnectTCP() - @@ -172,21 +342,9 @@ bool CSimpleSocket::ConnectTCP(const char *pAddr, uint16 nPort) memset(&m_stServerSockaddr, 0, sizeof(m_stServerSockaddr)); m_stServerSockaddr.sin_family = AF_INET; - struct hostent *pHE = GETHOSTBYNAME(pAddr); - if (pHE == NULL) - { -#ifdef WIN32 - TranslateSocketError(); -#else - if (h_errno == HOST_NOT_FOUND) - { - SetSocketError(SocketInvalidAddress); - } -#endif - return bRetVal; - } + if ( ! GetAddrInfo(pAddr, nPort, &stIpAddress) ) + return bRetVal; - memcpy(&stIpAddress, pHE->h_addr_list[0], pHE->h_length); m_stServerSockaddr.sin_addr.s_addr = stIpAddress.s_addr; if ((int32)m_stServerSockaddr.sin_addr.s_addr == CSimpleSocket::SocketError) @@ -260,21 +418,9 @@ bool CSimpleSocket::ConnectUDP(const char *pAddr, uint16 nPort) memset(&m_stServerSockaddr, 0, sizeof(m_stServerSockaddr)); m_stServerSockaddr.sin_family = AF_INET; - struct hostent *pHE = GETHOSTBYNAME(pAddr); - if (pHE == NULL) - { -#ifdef WIN32 - TranslateSocketError(); -#else - if (h_errno == HOST_NOT_FOUND) - { - SetSocketError(SocketInvalidAddress); - } -#endif - return bRetVal; - } + if ( ! GetAddrInfo(pAddr, nPort, &stIpAddress) ) + return bRetVal; - memcpy(&stIpAddress, pHE->h_addr_list[0], pHE->h_length); m_stServerSockaddr.sin_addr.s_addr = stIpAddress.s_addr; if ((int32)m_stServerSockaddr.sin_addr.s_addr == CSimpleSocket::SocketError) @@ -292,11 +438,8 @@ bool CSimpleSocket::ConnectUDP(const char *pAddr, uint16 nPort) m_timer.Initialize(); m_timer.SetStartTime(); - if (connect(m_socket, (struct sockaddr*)&m_stServerSockaddr, sizeof(m_stServerSockaddr)) != CSimpleSocket::SocketError) - { - bRetVal = true; - m_bPeerHasClosed = false; - } + bRetVal = true; + m_bPeerHasClosed = false; TranslateSocketError(); @@ -340,8 +483,32 @@ bool CSimpleSocket::ConnectRAW(const char *pAddr, uint16 nPort) return bRetVal; } - memcpy(&stIpAddress, pHE->h_addr_list[0], pHE->h_length); - m_stServerSockaddr.sin_addr.s_addr = stIpAddress.s_addr; + if ( pHE->h_addrtype == AF_INET && (int)sizeof(stIpAddress) >= pHE->h_length ) + { + memcpy(&stIpAddress, pHE->h_addr_list[0], pHE->h_length); + m_stServerSockaddr.sin_addr.s_addr = stIpAddress.s_addr; + } + else + { +#if CHECK_PRINT_RUNTIME_ERR + fprintf(stderr, "\nERROR at ConnectRAW(): hostent * result of gethostbyname(\"%s\"): h_addrtype = %s , sizeof(in_addr) = %d < %d = h_length !\n\n" + , pAddr + , ( pHE->h_addrtype == AF_INET ? "IPv4" : ( pHE->h_addrtype == AF_INET6 ? "IPv6!" : "unknown" ) ) + , (int)sizeof(stIpAddress) + , pHE->h_length + ); + fprintf(stdout, "\nERROR at ConnectRAW(): hostent * result of gethostbyname(\"%s\"): h_addrtype = %s , sizeof(in_addr) = %d < %d = h_length !\n\n" + , pAddr + , ( pHE->h_addrtype == AF_INET ? "IPv4" : ( pHE->h_addrtype == AF_INET6 ? "IPv6!" : "unknown" ) ) + , (int)sizeof(stIpAddress) + , pHE->h_length + ); + fflush(stdout); + CoreDump(); + assert(0); // check, when this happens +#endif + memset(&m_stServerSockaddr.sin_addr.s_addr, CSimpleSocket::SocketError, sizeof(m_stServerSockaddr.sin_addr.s_addr)); + } if ((int32)m_stServerSockaddr.sin_addr.s_addr == CSimpleSocket::SocketError) { @@ -420,6 +587,7 @@ bool CSimpleSocket::Initialize() //------------------------------------------------------------------------------ // // Open() - Create a connection to a specified address on a specified port +// also see .h // //------------------------------------------------------------------------------ bool CSimpleSocket::Open(const char *pAddr, uint16 nPort) @@ -472,10 +640,14 @@ bool CSimpleSocket::Open(const char *pAddr, uint16 nPort) //-------------------------------------------------------------------------- if (bRetVal) { - socklen_t nSockLen = sizeof(struct sockaddr); + socklen_t nSockLen; + if(m_nSocketType != CSimpleSocket::SocketTypeUdp) + { + nSockLen = sizeof(struct sockaddr); - memset(&m_stServerSockaddr, 0, nSockLen); - getpeername(m_socket, (struct sockaddr *)&m_stServerSockaddr, &nSockLen); + memset(&m_stServerSockaddr, 0, nSockLen); + getpeername(m_socket, (struct sockaddr *)&m_stServerSockaddr, &nSockLen); + } nSockLen = sizeof(struct sockaddr); memset(&m_stClientSockaddr, 0, nSockLen); @@ -502,7 +674,7 @@ bool CSimpleSocket::BindInterface(const char *pInterface) { if (pInterface) { - stInterfaceAddr.s_addr= inet_addr(pInterface); + inet_pton(AF_INET, pInterface, &stInterfaceAddr.s_addr); if (SETSOCKOPT(m_socket, IPPROTO_IP, IP_MULTICAST_IF, &stInterfaceAddr, sizeof(stInterfaceAddr)) == SocketSuccess) { bRetVal = true; @@ -677,7 +849,7 @@ uint32 CSimpleSocket::GetWindowSize(uint32 nOptionName) //------------------------------------------------------------------------- // no socket given, return system default allocate our own new socket //------------------------------------------------------------------------- - if (m_socket != CSimpleSocket::SocketError) + if (m_socket != INVALID_SOCKET) { socklen_t nLen = sizeof(nTcpWinSize); @@ -708,7 +880,7 @@ uint32 CSimpleSocket::SetWindowSize(uint32 nOptionName, uint32 nWindowSize) //------------------------------------------------------------------------- // no socket given, return system default allocate our own new socket //------------------------------------------------------------------------- - if (m_socket != CSimpleSocket::SocketError) + if (m_socket != INVALID_SOCKET) { if (SETSOCKOPT(m_socket, SOL_SOCKET, nOptionName, &nWindowSize, sizeof(nWindowSize)) == SocketError) { @@ -778,6 +950,26 @@ bool CSimpleSocket::EnableNagleAlgoritm() } +uint32 CSimpleSocket::GetIPv4AddrInfoStatic( const char *pAddr, CSocketType nSocketType ) +{ + struct in_addr stIpAddress; + bool ret = GetAddrInfoStatic( pAddr, 0, &stIpAddress, nSocketType ); + if (!ret) + return 0; + return ntohl(stIpAddress.s_addr); +} + + +uint32 CSimpleSocket::GetIPv4AddrInfo( const char *pAddr ) +{ + struct in_addr stIpAddress; + bool ret = GetAddrInfo( pAddr, 0, &stIpAddress ); + if (!ret) + return 0; + return ntohl(stIpAddress.s_addr); +} + + /// Set object socket handle to that specified as parameter /// @param socket value of socket descriptor void CSimpleSocket::SetSocketHandle(SOCKET socket, bool bIsServerSide ) @@ -920,7 +1112,7 @@ bool CSimpleSocket::Shutdown(CShutdownMode nShutdown) { CSocketError nRetVal = SocketEunknown; - nRetVal = (CSocketError)shutdown(m_socket, CSimpleSocket::Sends); + nRetVal = (CSocketError)shutdown(m_socket, nShutdown); if (nRetVal == SocketError) { TranslateSocketError(); @@ -1458,17 +1650,17 @@ int32 CSimpleSocket::SendFile(int32 nOutFd, int32 nInFd, off_t *pOffset, int32 n } -bool CSimpleSocket::IsSocketValid(void) +bool CSimpleSocket::IsSocketValid(void) const { #if PRINT_CLOSE if ( m_socket == SocketError ) fprintf(stderr, "reporting CSimpleSocket::IsSocketValid() == false.\n" ); #endif - return (m_socket != SocketError); + return (m_socket != INVALID_SOCKET); } -bool CSimpleSocket::IsSocketPeerClosed(void) +bool CSimpleSocket::IsSocketPeerClosed(void) const { #if PRINT_CLOSE if ( m_bPeerHasClosed ) @@ -1503,6 +1695,11 @@ void CSimpleSocket::ClearSystemError(void) //------------------------------------------------------------------------------ void CSimpleSocket::TranslateSocketError(void) { + // see + // * http://man7.org/linux/man-pages/man3/errno.3.html + // * https://msdn.microsoft.com/en-us/library/windows/desktop/ms740668(v=vs.85).aspx# + // for the system's error code's + // it's quite difficult to map all of them to some CSocketError! #if defined(_LINUX) || defined(_DARWIN) int systemErrno = errno; switch (systemErrno) @@ -1558,9 +1755,16 @@ void CSimpleSocket::TranslateSocketError(void) case ENOPROTOOPT: SetSocketError(CSimpleSocket::SocketConnectionReset); break; + case EADDRNOTAVAIL: case EADDRINUSE: SetSocketError(CSimpleSocket::SocketAddressInUse); break; + case ESHUTDOWN: + case EHOSTUNREACH: + case ENETUNREACH: + case ENETDOWN: + SetSocketError(CSimpleSocket::SocketNetworkError); + break; default: #if PRINT_ERRORS fprintf( stderr, "\nCSimpleSocket::SocketEunknown: errno=%d: %s\n", systemErrno, strerror(systemErrno) ); @@ -1622,11 +1826,18 @@ void CSimpleSocket::TranslateSocketError(void) SetSocketError(CSimpleSocket::SocketInvalidAddress); break; case WSAEADDRINUSE: + case WSAEADDRNOTAVAIL: SetSocketError(CSimpleSocket::SocketAddressInUse); break; case WSAEFAULT: SetSocketError(CSimpleSocket::SocketInvalidPointer); break; + case WSAENETDOWN: + case WSAENETUNREACH: + case WSAENETRESET: + case WSAESHUTDOWN: + SetSocketError(CSimpleSocket::SocketNetworkError); + break; default: SetSocketError(CSimpleSocket::SocketEunknown); break; @@ -1683,6 +1894,8 @@ const char *CSimpleSocket::DescribeError(CSocketError err) return "Pointer type supplied as argument is invalid."; case CSimpleSocket::SocketEunknown: return "Unknown error"; + case CSimpleSocket::SocketNetworkError: + return "Network error"; default: return "No such CSimpleSocket error"; } diff --git a/src/SimpleSocket.h b/src/SimpleSocket.h index e004360..d3f1b90 100644 --- a/src/SimpleSocket.h +++ b/src/SimpleSocket.h @@ -150,9 +150,10 @@ class CSimpleSocket { SocketInvalidPort, ///< Invalid destination port specified. SocketConnectionRefused, ///< No server is listening at remote address. SocketTimedout, ///< Timed out while attempting operation. - SocketEwouldblock, ///< Operation would block if socket were blocking. + SocketEwouldblock, ///< Operation would block if socket were blocking. On Accept/Receive/Send/ SocketNotconnected, ///< Currently not connected. - SocketEinprogress, ///< Socket is non-blocking and the connection cannot be completed immediately + SocketEinprogress, ///< Socket is non-blocking and the connection cannot be completed immediately. + /// only on Open/ConnectTo/.. SocketInterrupted, ///< Call was interrupted by a signal that was caught before a valid connection arrived. SocketConnectionAborted, ///< The connection has been aborted. SocketProtocolError, ///< Invalid protocol for operation. @@ -161,7 +162,8 @@ class CSimpleSocket { SocketConnectionReset, ///< Connection was forcibly closed by the remote host. SocketAddressInUse, ///< Address already in use. SocketInvalidPointer, ///< Pointer type supplied as argument is invalid. - SocketEunknown ///< Unknown error please report to mark@carrierlabs.com + SocketEunknown, ///< Unknown error + SocketNetworkError ///< Several network errors } CSocketError; public: @@ -176,7 +178,8 @@ class CSimpleSocket { /// @return true if properly initialized. virtual bool Initialize(void); - /// Established a connection to the address specified by pAddr. + /// Establishes a connection to the (server-)address specified by pAddr + /// and (server-)port specified by nPort. /// Connection-based protocol sockets (CSocket::SocketTypeTcp) may /// successfully call Open() only once, however; connectionless protocol /// sockets (CSocket::SocketTypeUdp) may use Open() multiple times to @@ -240,10 +243,10 @@ class CSimpleSocket { /// Does the current instance of the socket object contain a valid socket /// descriptor. /// @return true if the socket object contains a valid socket descriptor. - virtual bool IsSocketValid(void); + virtual bool IsSocketValid(void) const; - inline bool IsSocketInvalid(void) { + inline bool IsSocketInvalid(void) const { return !IsSocketValid(); }; @@ -251,10 +254,10 @@ class CSimpleSocket { /// Is the current instance of the socket already closed from peer? /// Information is updated on Receive() ! /// @return true if the socket was closed - virtual bool IsSocketPeerClosed(void); + virtual bool IsSocketPeerClosed(void) const; - inline bool IsSocketPeerOpen(void) { + inline bool IsSocketPeerOpen(void) const { return !IsSocketPeerClosed(); }; @@ -628,6 +631,14 @@ class CSimpleSocket { /// @return false if failed to set socket option otherwise return true; bool EnableNagleAlgoritm(); + /// retrieve IPv4 address via getaddrinfo() as uint32 in HostByteOrder + /// as static member + /// @return 0 if failed + static uint32 GetIPv4AddrInfoStatic( const char *pAddr, CSocketType nSocketType = SocketTypeTcp ); + + /// retrieve IPv4 address via getaddrinfo() as uint32 in HostByteOrder + /// @return 0 if failed + uint32 GetIPv4AddrInfo( const char *pAddr ); protected: /// Set internal socket error to that specified error @@ -667,6 +678,10 @@ class CSimpleSocket { CSimpleSocket *operator=(CSimpleSocket &socket); + static bool GetAddrInfoStatic(const char *pAddr, uint16 nPort, struct in_addr * pOutIpAddress, CSocketType nSocketType = SocketTypeTcp ); + + bool GetAddrInfo(const char *pAddr, uint16 nPort, struct in_addr * pOutIpAddress ); + /// Utility function used to create a TCP connection, called from Open(). /// @return true if successful connection made, otherwise false. bool ConnectTCP(const char *pAddr, uint16 nPort);