From edf4331d1a94835af850b07cb8f3c604b6bbd2ad Mon Sep 17 00:00:00 2001 From: kervala Date: Sun, 14 Feb 2016 18:21:09 +0100 Subject: [PATCH] Fixed: Implements IPv6 in CInetAddress --HG-- branch : develop --- code/nel/include/nel/net/inet_address.h | 20 ++- code/nel/src/net/inet_address.cpp | 198 +++++++++++++++++++----- 2 files changed, 173 insertions(+), 45 deletions(-) diff --git a/code/nel/include/nel/net/inet_address.h b/code/nel/include/nel/net/inet_address.h index 5e8046b55..dcf5b3708 100644 --- a/code/nel/include/nel/net/inet_address.h +++ b/code/nel/include/nel/net/inet_address.h @@ -24,7 +24,9 @@ struct sockaddr_in; +struct sockaddr_in6; struct in_addr; +struct in6_addr; #ifdef NL_OS_WINDOWS @@ -89,17 +91,25 @@ public: /// Sets hostname and port (ex: www.nevrax.com:80) void setNameAndPort( const std::string& hostNameAndPort ); - /** Sets internal socket address directly (contents is copied). + /** Sets internal IPv4 socket address directly (contents is copied). * It also retrieves the host name if CInetAddress::RetrieveNames is true. */ void setSockAddr( const sockaddr_in* saddr ); + /** Sets internal IPv6 socket address directly (contents is copied). + * It also retrieves the host name if CInetAddress::RetrieveNames is true. + */ + void setSockAddr6( const sockaddr_in6* saddr6 ); + /// Returns if object (address and port) is valid bool isValid() const; - /// Returns internal socket address (read only) + /// Returns internal IPv4 socket address (read only) const sockaddr_in *sockAddr() const; + /// Returns internal IPv6 socket address (read only) + const sockaddr_in6 *sockAddr6() const; + /// Returns internal IP address uint32 internalIPAddress() const; @@ -140,9 +150,12 @@ public: protected: - /// Constructor with ip address, port=0 + /// Constructor with IPv4 address, port=0 CInetAddress( const in_addr *ip, const char *hostname = 0); + /// Constructor with IPv6 address, port=0 + CInetAddress( const in6_addr *ip, const char *hostname = 0); + /// Update _HostName from _SockAddr void updateHostName(); @@ -153,6 +166,7 @@ private: std::string _HostName; sockaddr_in *_SockAddr; + sockaddr_in6 *_SockAddr6; bool _Valid; }; diff --git a/code/nel/src/net/inet_address.cpp b/code/nel/src/net/inet_address.cpp index 9c3bb3984..45ff0c20e 100644 --- a/code/nel/src/net/inet_address.cpp +++ b/code/nel/src/net/inet_address.cpp @@ -27,6 +27,7 @@ #ifdef NL_OS_WINDOWS # include # include +# include // for Windows 2000 compatibility # include #elif defined NL_OS_UNIX @@ -58,20 +59,32 @@ bool CInetAddress::RetrieveNames = false; CInetAddress::CInetAddress() { init(); + + // IPv4 _SockAddr->sin_port = 0; // same as htons(0) memset( &_SockAddr->sin_addr, 0, sizeof(in_addr) ); // same as htonl(INADDR_ANY) + + // IPv6 + _SockAddr6->sin6_port = 0; + memset( &_SockAddr6->sin6_addr, 0, sizeof(in6_addr) ); // same as htonl(INADDR_ANY) } /* - * Constructor with ip address, port=0 + * Constructor with IPv4 address, port=0 */ CInetAddress::CInetAddress( const in_addr *ip, const char *hostname ) { init(); + + // IPv4 _SockAddr->sin_port = 0; memcpy( &_SockAddr->sin_addr, ip, sizeof(in_addr) ); + // invalid IPv6 + _SockAddr6->sin6_port = 0; + memset( &_SockAddr6->sin6_addr, 0, sizeof(in6_addr) ); + // get the host name to be displayed if(hostname) { @@ -81,6 +94,36 @@ CInetAddress::CInetAddress( const in_addr *ip, const char *hostname ) { updateHostName(); } + + _Valid = true; +} + + +/* + * Constructor with IPv6 address, port=0 + */ +CInetAddress::CInetAddress( const in6_addr *ip, const char *hostname ) +{ + init(); + + // IPv6 + _SockAddr6->sin6_port = 0; + memcpy( &_SockAddr6->sin6_addr, ip, sizeof(in6_addr) ); + + // invalid IPv4 + _SockAddr->sin_port = 0; + memset( &_SockAddr->sin_addr, 0, sizeof(in_addr) ); + + // get the host name to be displayed + if(hostname) + { + _HostName = hostname; + } + else + { + updateHostName(); + } + _Valid = true; } @@ -92,7 +135,20 @@ void CInetAddress::updateHostName() { char host[NI_MAXHOST]; - sint status = getnameinfo((struct sockaddr *) _SockAddr, sizeof (struct sockaddr), host, NI_MAXHOST, NULL, 0, NI_NUMERICSERV); + // if unable to resolve DNS, returns an error and use IP address instead + sint status = 1; + + // check if IPv4 is valid + if (_SockAddr->sin_addr.s_addr != 0) + { + // IPv4 + status = getnameinfo((struct sockaddr *) _SockAddr, sizeof (sockaddr_in), host, NI_MAXHOST, NULL, 0, NI_NUMERICSERV | NI_NAMEREQD); + } + else if (!IN6_IS_ADDR_UNSPECIFIED(&_SockAddr6->sin6_addr)) + { + // IPv6 + status = getnameinfo((struct sockaddr *) _SockAddr6, sizeof (sockaddr_in6), host, NI_MAXHOST, NULL, 0, NI_NUMERICSERV | NI_NAMEREQD); + } if ( status ) { @@ -134,6 +190,7 @@ CInetAddress::CInetAddress( const CInetAddress& other ) init(); _HostName = other._HostName; memcpy( _SockAddr, other._SockAddr, sizeof( *_SockAddr ) ); + memcpy( _SockAddr6, other._SockAddr6, sizeof( *_SockAddr6 ) ); _Valid = other._Valid; } @@ -145,6 +202,7 @@ CInetAddress& CInetAddress::operator=( const CInetAddress& other ) { _HostName = other._HostName; memcpy( _SockAddr, other._SockAddr, sizeof( *_SockAddr ) ); + memcpy( _SockAddr6, other._SockAddr6, sizeof( *_SockAddr6 ) ); _Valid = other._Valid; return *this; } @@ -185,9 +243,15 @@ void CInetAddress::init() _Valid = false; + // IPv4 _SockAddr = new sockaddr_in; + memset(_SockAddr, 0, sizeof(_SockAddr)); _SockAddr->sin_family = AF_INET; - memset( _SockAddr->sin_zero, 0, 8 ); + + // IPv6 + _SockAddr6 = new sockaddr_in6; + memset(_SockAddr6, 0, sizeof(_SockAddr6)); + _SockAddr6->sin6_family = AF_INET6; } @@ -197,6 +261,7 @@ void CInetAddress::init() CInetAddress::~CInetAddress() { delete _SockAddr; + delete _SockAddr6; // _Valid = false; } @@ -225,12 +290,41 @@ void CInetAddress::setNameAndPort( const std::string& hostNameAndPort ) /* * Resolves a name */ -CInetAddress& CInetAddress::setByName( const std::string& hostName ) +CInetAddress& CInetAddress::setByName(const std::string& hostName) { - // Try to convert directly for addresses such as a.b.c.d - in_addr iaddr; - iaddr.s_addr = inet_addr( hostName.c_str() ); - if ( iaddr.s_addr == INADDR_NONE ) + // invalid IPv4 + memset(&_SockAddr->sin_addr, 0, sizeof(in_addr)); + + // invalid IPv6 + memset(&_SockAddr6->sin6_addr, 0, sizeof(in6_addr)); + + // Try to convert directly for addresses such as a.b.c.d and a:b:c:d:e:f:g:h + in_addr ipv4; + sint res = inet_pton(AF_INET, hostName.c_str(), &ipv4); + + if (res == 1) + { + // hostname is a valid IPv4 + memcpy(&_SockAddr->sin_addr, &ipv4, sizeof(in_addr)); + } + else + { + in6_addr ipv6; + res = inet_pton(AF_INET6, hostName.c_str(), &ipv6); + + if (res == 1) + { + // hostname is a valid IPv6 + memcpy(&_SockAddr6->sin6_addr, &ipv6, sizeof(in6_addr)); + } + } + + if (res == 1) + { + // use IPv4 or IPv6 as hostname + _HostName = hostName; + } + else { // Otherwise use the traditional DNS look-up struct addrinfo hints; @@ -249,6 +343,9 @@ CInetAddress& CInetAddress::setByName( const std::string& hostName ) throw ESocket( (string("Hostname resolution failed for ")+hostName).c_str() ); } + // hostname is valid, use it + _HostName = hostName; + struct addrinfo *p = res; // process all addresses @@ -260,20 +357,14 @@ CInetAddress& CInetAddress::setByName( const std::string& hostName ) // ipv4 struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr; - // convert the IP to a string - _HostName = string(inet_ntoa(ipv4->sin_addr)); memcpy( &_SockAddr->sin_addr, &ipv4->sin_addr, sizeof(in_addr) ); } else if (p->ai_family == AF_INET6) { // ipv6 - // TODO: modify class to be able to handle IPv6 - - // struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr; + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr; - // convert the IP to a string - // inet_ntop(p->ai_family, addr, ipstr, sizeof(ipstr)); - // memcpy( &_SockAddr->sin_addr, &ipv6->sin_addr, sizeof(in_addr) ); + memcpy( &_SockAddr6->sin6_addr, &ipv6->sin6_addr, sizeof(in6_addr) ); } // process next address @@ -283,11 +374,7 @@ CInetAddress& CInetAddress::setByName( const std::string& hostName ) // free the linked list freeaddrinfo(res); } - else - { - _HostName = hostName; - memcpy( &_SockAddr->sin_addr, &iaddr, sizeof(iaddr) ); - } + _Valid = true; return *this; } @@ -296,10 +383,10 @@ CInetAddress& CInetAddress::setByName( const std::string& hostName ) /* * Sets port */ -void CInetAddress::setPort( uint16 port ) +void CInetAddress::setPort(uint16 port) { - _SockAddr->sin_port = htons( port ); - + _SockAddr->sin_port = htons(port); + _SockAddr6->sin6_port = htons(port); } @@ -308,7 +395,31 @@ void CInetAddress::setPort( uint16 port ) */ void CInetAddress::setSockAddr( const sockaddr_in* saddr ) { - memcpy( _SockAddr, saddr, sizeof(*saddr) ); + memcpy(_SockAddr, saddr, sizeof(*saddr) ); + + // invalid IPv6 + memset(&_SockAddr6->sin6_addr, 0, sizeof(in6_addr)); + + // Get host name + // Warning: when it can't find it, it take more than 4 seconds + if ( CInetAddress::RetrieveNames ) + { + updateHostName(); + } + + _Valid = true; +} + + +/* Sets internal socket address directly (contents is copied). + * It also retrieves the host name if CInetAddress::RetrieveNames is true. + */ +void CInetAddress::setSockAddr6( const sockaddr_in6* saddr6 ) +{ + memcpy( _SockAddr6, saddr6, sizeof(*saddr6) ); + + // invalid IPv4 + memset(&_SockAddr->sin_addr, 0, sizeof(in_addr)); // Get host name // Warning: when it can't find it, it take more than 4 seconds @@ -316,6 +427,7 @@ void CInetAddress::setSockAddr( const sockaddr_in* saddr ) { updateHostName(); } + _Valid = true; } @@ -330,7 +442,7 @@ bool CInetAddress::isValid() const /* - * Returns internal socket address (read only) + * Returns internal IPv4 socket address (read only) */ const sockaddr_in *CInetAddress::sockAddr() const { @@ -338,6 +450,15 @@ const sockaddr_in *CInetAddress::sockAddr() const } +/* + * Returns internal IPv6 socket address (read only) + */ +const sockaddr_in6 *CInetAddress::sockAddr6() const +{ + return _SockAddr6; +} + + /* * Returns internal IP address */ @@ -383,10 +504,13 @@ uint32 CInetAddress::internalNetAddress() const */ string CInetAddress::ipAddress() const { - /*stringstream ss; // or use inet_ntoa - ss << inet_ntoa ( _SockAddr->sin_addr ); - return ss.str();*/ - const char *name = inet_ntoa ( _SockAddr->sin_addr ); + // longer size is IPv6 + char straddr[INET6_ADDRSTRLEN]; + const char *name = inet_ntop(AF_INET, &_SockAddr->sin_addr, straddr, INET_ADDRSTRLEN); + + // IPv4 is invalid, return IPv6 + if (name == NULL || strcmp(name, "0.0.0.0") == 0) name = inet_ntop(AF_INET6, &_SockAddr6->sin6_addr, straddr, INET6_ADDRSTRLEN); + return name ? string (name) : ""; } @@ -414,9 +538,6 @@ uint16 CInetAddress::port() const */ std::string CInetAddress::asString() const { -// stringstream ss; -// ss << hostName() << ":" << port() << " (" << ipAddress() << ")"; -// return ss.str(); return hostName() + ":" + NLMISC::toString(port()) + " (" + ipAddress() + ")"; } @@ -426,9 +547,6 @@ std::string CInetAddress::asString() const */ std::string CInetAddress::asIPString() const { -// stringstream ss; -// ss << ipAddress() << ":" << port(); -// return ss.str(); return ipAddress() + ":" + NLMISC::toString(port()); } @@ -537,13 +655,9 @@ std::vector CInetAddress::localAddresses() else if (p->ai_family == AF_INET6) { // ipv6 - // TODO: modify class to be able to handle IPv6 - - // struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr; + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr; - // convert the IP to a string - // inet_ntop(p->ai_family, addr, ipstr, sizeof(ipstr)); - // memcpy( &_SockAddr->sin_addr, &ipv6->sin_addr, sizeof(in_addr) ); + vect.push_back( CInetAddress( &ipv6->sin6_addr, localhost ) ); } // process next address