Fixed: Implements IPv6 in CInetAddress

This commit is contained in:
kervala 2016-02-14 18:21:09 +01:00
parent a16f8a6f18
commit c63e211cab
2 changed files with 173 additions and 45 deletions

View file

@ -24,7 +24,9 @@
struct sockaddr_in; struct sockaddr_in;
struct sockaddr_in6;
struct in_addr; struct in_addr;
struct in6_addr;
#ifdef NL_OS_WINDOWS #ifdef NL_OS_WINDOWS
@ -89,17 +91,25 @@ public:
/// Sets hostname and port (ex: www.nevrax.com:80) /// Sets hostname and port (ex: www.nevrax.com:80)
void setNameAndPort( const std::string& hostNameAndPort ); 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. * It also retrieves the host name if CInetAddress::RetrieveNames is true.
*/ */
void setSockAddr( const sockaddr_in* saddr ); 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 /// Returns if object (address and port) is valid
bool isValid() const; bool isValid() const;
/// Returns internal socket address (read only) /// Returns internal IPv4 socket address (read only)
const sockaddr_in *sockAddr() const; const sockaddr_in *sockAddr() const;
/// Returns internal IPv6 socket address (read only)
const sockaddr_in6 *sockAddr6() const;
/// Returns internal IP address /// Returns internal IP address
uint32 internalIPAddress() const; uint32 internalIPAddress() const;
@ -140,9 +150,12 @@ public:
protected: protected:
/// Constructor with ip address, port=0 /// Constructor with IPv4 address, port=0
CInetAddress( const in_addr *ip, const char *hostname = 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 /// Update _HostName from _SockAddr
void updateHostName(); void updateHostName();
@ -153,6 +166,7 @@ private:
std::string _HostName; std::string _HostName;
sockaddr_in *_SockAddr; sockaddr_in *_SockAddr;
sockaddr_in6 *_SockAddr6;
bool _Valid; bool _Valid;
}; };

View file

@ -27,6 +27,7 @@
#ifdef NL_OS_WINDOWS #ifdef NL_OS_WINDOWS
# include <winsock2.h> # include <winsock2.h>
# include <ws2tcpip.h> # include <ws2tcpip.h>
# include <ws2ipdef.h>
// for Windows 2000 compatibility // for Windows 2000 compatibility
# include <wspiapi.h> # include <wspiapi.h>
#elif defined NL_OS_UNIX #elif defined NL_OS_UNIX
@ -58,20 +59,32 @@ bool CInetAddress::RetrieveNames = false;
CInetAddress::CInetAddress() CInetAddress::CInetAddress()
{ {
init(); init();
// IPv4
_SockAddr->sin_port = 0; // same as htons(0) _SockAddr->sin_port = 0; // same as htons(0)
memset( &_SockAddr->sin_addr, 0, sizeof(in_addr) ); // same as htonl(INADDR_ANY) 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 ) CInetAddress::CInetAddress( const in_addr *ip, const char *hostname )
{ {
init(); init();
// IPv4
_SockAddr->sin_port = 0; _SockAddr->sin_port = 0;
memcpy( &_SockAddr->sin_addr, ip, sizeof(in_addr) ); 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 // get the host name to be displayed
if(hostname) if(hostname)
{ {
@ -81,6 +94,36 @@ CInetAddress::CInetAddress( const in_addr *ip, const char *hostname )
{ {
updateHostName(); 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; _Valid = true;
} }
@ -92,7 +135,20 @@ void CInetAddress::updateHostName()
{ {
char host[NI_MAXHOST]; 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 ) if ( status )
{ {
@ -134,6 +190,7 @@ CInetAddress::CInetAddress( const CInetAddress& other )
init(); init();
_HostName = other._HostName; _HostName = other._HostName;
memcpy( _SockAddr, other._SockAddr, sizeof( *_SockAddr ) ); memcpy( _SockAddr, other._SockAddr, sizeof( *_SockAddr ) );
memcpy( _SockAddr6, other._SockAddr6, sizeof( *_SockAddr6 ) );
_Valid = other._Valid; _Valid = other._Valid;
} }
@ -145,6 +202,7 @@ CInetAddress& CInetAddress::operator=( const CInetAddress& other )
{ {
_HostName = other._HostName; _HostName = other._HostName;
memcpy( _SockAddr, other._SockAddr, sizeof( *_SockAddr ) ); memcpy( _SockAddr, other._SockAddr, sizeof( *_SockAddr ) );
memcpy( _SockAddr6, other._SockAddr6, sizeof( *_SockAddr6 ) );
_Valid = other._Valid; _Valid = other._Valid;
return *this; return *this;
} }
@ -185,9 +243,15 @@ void CInetAddress::init()
_Valid = false; _Valid = false;
// IPv4
_SockAddr = new sockaddr_in; _SockAddr = new sockaddr_in;
memset(_SockAddr, 0, sizeof(_SockAddr));
_SockAddr->sin_family = AF_INET; _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() CInetAddress::~CInetAddress()
{ {
delete _SockAddr; delete _SockAddr;
delete _SockAddr6;
// _Valid = false; // _Valid = false;
} }
@ -225,12 +290,41 @@ void CInetAddress::setNameAndPort( const std::string& hostNameAndPort )
/* /*
* Resolves a name * 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 // invalid IPv4
in_addr iaddr; memset(&_SockAddr->sin_addr, 0, sizeof(in_addr));
iaddr.s_addr = inet_addr( hostName.c_str() );
if ( iaddr.s_addr == INADDR_NONE ) // 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 // Otherwise use the traditional DNS look-up
struct addrinfo hints; struct addrinfo hints;
@ -249,6 +343,9 @@ CInetAddress& CInetAddress::setByName( const std::string& hostName )
throw ESocket( (string("Hostname resolution failed for ")+hostName).c_str() ); throw ESocket( (string("Hostname resolution failed for ")+hostName).c_str() );
} }
// hostname is valid, use it
_HostName = hostName;
struct addrinfo *p = res; struct addrinfo *p = res;
// process all addresses // process all addresses
@ -260,20 +357,14 @@ CInetAddress& CInetAddress::setByName( const std::string& hostName )
// ipv4 // ipv4
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr; 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) ); memcpy( &_SockAddr->sin_addr, &ipv4->sin_addr, sizeof(in_addr) );
} }
else if (p->ai_family == AF_INET6) else if (p->ai_family == AF_INET6)
{ {
// ipv6 // 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; memcpy( &_SockAddr6->sin6_addr, &ipv6->sin6_addr, sizeof(in6_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) );
} }
// process next address // process next address
@ -283,11 +374,7 @@ CInetAddress& CInetAddress::setByName( const std::string& hostName )
// free the linked list // free the linked list
freeaddrinfo(res); freeaddrinfo(res);
} }
else
{
_HostName = hostName;
memcpy( &_SockAddr->sin_addr, &iaddr, sizeof(iaddr) );
}
_Valid = true; _Valid = true;
return *this; return *this;
} }
@ -296,10 +383,10 @@ CInetAddress& CInetAddress::setByName( const std::string& hostName )
/* /*
* Sets port * 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,10 @@ void CInetAddress::setPort( uint16 port )
*/ */
void CInetAddress::setSockAddr( const sockaddr_in* saddr ) 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 // Get host name
// Warning: when it can't find it, it take more than 4 seconds // Warning: when it can't find it, it take more than 4 seconds
@ -316,6 +406,28 @@ void CInetAddress::setSockAddr( const sockaddr_in* saddr )
{ {
updateHostName(); 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
if ( CInetAddress::RetrieveNames )
{
updateHostName();
}
_Valid = true; _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 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 * Returns internal IP address
*/ */
@ -383,10 +504,13 @@ uint32 CInetAddress::internalNetAddress() const
*/ */
string CInetAddress::ipAddress() const string CInetAddress::ipAddress() const
{ {
/*stringstream ss; // or use inet_ntoa // longer size is IPv6
ss << inet_ntoa ( _SockAddr->sin_addr ); char straddr[INET6_ADDRSTRLEN];
return ss.str();*/ const char *name = inet_ntop(AF_INET, &_SockAddr->sin_addr, straddr, INET_ADDRSTRLEN);
const char *name = inet_ntoa ( _SockAddr->sin_addr );
// 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) : ""; return name ? string (name) : "";
} }
@ -414,9 +538,6 @@ uint16 CInetAddress::port() const
*/ */
std::string CInetAddress::asString() const std::string CInetAddress::asString() const
{ {
// stringstream ss;
// ss << hostName() << ":" << port() << " (" << ipAddress() << ")";
// return ss.str();
return hostName() + ":" + NLMISC::toString(port()) + " (" + ipAddress() + ")"; return hostName() + ":" + NLMISC::toString(port()) + " (" + ipAddress() + ")";
} }
@ -426,9 +547,6 @@ std::string CInetAddress::asString() const
*/ */
std::string CInetAddress::asIPString() const std::string CInetAddress::asIPString() const
{ {
// stringstream ss;
// ss << ipAddress() << ":" << port();
// return ss.str();
return ipAddress() + ":" + NLMISC::toString(port()); return ipAddress() + ":" + NLMISC::toString(port());
} }
@ -537,13 +655,9 @@ std::vector<CInetAddress> CInetAddress::localAddresses()
else if (p->ai_family == AF_INET6) else if (p->ai_family == AF_INET6)
{ {
// ipv6 // 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; vect.push_back( CInetAddress( &ipv6->sin6_addr, localhost ) );
// 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) );
} }
// process next address // process next address