Merge branch 'fixes' into main/rendor-staging
[ryzomcore.git] / nel / src / net / inet_address.cpp
blob13ae02c3990c6ee17d6eda01a7daa7fd6488b25a
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 #include "stdnet.h"
19 #include "nel/misc/common.h"
21 #include "nel/net/inet_address.h"
22 #include "nel/net/sock.h"
23 #include "nel/net/net_log.h"
27 #ifdef NL_OS_WINDOWS
28 # include <winsock2.h>
29 # include <ws2tcpip.h>
30 # include <ws2ipdef.h>
31 // for Windows 2000 compatibility
32 # include <wspiapi.h>
34 #if !defined(NTDDI_VISTA) || (NTDDI_VERSION < NTDDI_VISTA)
36 // inet_pton and inet_ntop not defined in winsock DLL before Vista
38 // taken from http://stackoverflow.com/questions/13731243/what-is-the-windows-xp-equivalent-of-inet-pton-or-inetpton
39 int inet_pton(int af, const char *src, void *dst)
41 struct sockaddr_storage ss;
42 int size = sizeof(ss);
43 char src_copy[INET6_ADDRSTRLEN+1];
45 ZeroMemory(&ss, sizeof(ss));
46 // stupid non-const API
47 strncpy (src_copy, src, INET6_ADDRSTRLEN+1);
48 src_copy[INET6_ADDRSTRLEN] = 0;
50 if (WSAStringToAddress(src_copy, af, NULL, (struct sockaddr *)&ss, &size) == 0)
52 switch(af)
54 case AF_INET:
55 *(struct in_addr *)dst = ((struct sockaddr_in *)&ss)->sin_addr;
56 return 1;
58 case AF_INET6:
59 *(struct in6_addr *)dst = ((struct sockaddr_in6 *)&ss)->sin6_addr;
60 return 1;
64 return 0;
67 const char *inet_ntop(int af, const void *src, char *dst, socklen_t size)
69 struct sockaddr_storage ss;
70 unsigned long s = size;
72 ZeroMemory(&ss, sizeof(ss));
73 ss.ss_family = af;
75 switch(af)
77 case AF_INET:
78 ((struct sockaddr_in *)&ss)->sin_addr = *(struct in_addr *)src;
79 break;
81 case AF_INET6:
82 ((struct sockaddr_in6 *)&ss)->sin6_addr = *(struct in6_addr *)src;
83 break;
85 default:
86 return NULL;
89 // cannot directly use &size because of strict aliasing rules
90 return WSAAddressToString((struct sockaddr *)&ss, sizeof(ss), NULL, dst, &s) == 0 ? dst : NULL;
93 BOOLEAN IN6_IS_ADDR_UNSPECIFIED(CONST IN6_ADDR *a)
96 // We can't use the in6addr_any variable, since that would
97 // require existing callers to link with a specific library.
99 return (BOOLEAN)((a->s6_words[0] == 0) &&
100 (a->s6_words[1] == 0) &&
101 (a->s6_words[2] == 0) &&
102 (a->s6_words[3] == 0) &&
103 (a->s6_words[4] == 0) &&
104 (a->s6_words[5] == 0) &&
105 (a->s6_words[6] == 0) &&
106 (a->s6_words[7] == 0));
109 #endif
111 #elif defined NL_OS_UNIX
112 # include <unistd.h>
113 # include <sys/socket.h>
114 # include <arpa/inet.h>
115 # include <netinet/in.h>
116 # include <netdb.h>
117 # define WSAGetLastError() 0
118 # define SOCKET_ERROR -1
119 #endif
121 using namespace std;
122 using namespace NLMISC;
124 #ifdef DEBUG_NEW
125 #define new DEBUG_NEW
126 #endif
128 #ifndef NI_MAXHOST
129 # define NI_MAXHOST 1025
130 #endif
132 namespace NLNET
135 bool CInetAddress::RetrieveNames = false;
139 * Constructor
141 CInetAddress::CInetAddress()
143 init();
145 // IPv4
146 _SockAddr->sin_port = 0; // same as htons(0)
147 memset( &_SockAddr->sin_addr, 0, sizeof(in_addr) ); // same as htonl(INADDR_ANY)
149 // IPv6
150 _SockAddr6->sin6_port = 0;
151 memset( &_SockAddr6->sin6_addr, 0, sizeof(in6_addr) ); // same as htonl(INADDR_ANY)
156 * Constructor with IPv4 address, port=0
158 CInetAddress::CInetAddress( const in_addr *ip, const char *hostname )
160 init();
162 // IPv4
163 _SockAddr->sin_port = 0;
164 memcpy( &_SockAddr->sin_addr, ip, sizeof(in_addr) );
166 // invalid IPv6
167 _SockAddr6->sin6_port = 0;
168 memset( &_SockAddr6->sin6_addr, 0, sizeof(in6_addr) );
170 // get the host name to be displayed
171 if(hostname)
173 _HostName = hostname;
175 else
177 updateHostName();
180 _Valid = true;
185 * Constructor with IPv6 address, port=0
187 CInetAddress::CInetAddress( const in6_addr *ip, const char *hostname )
189 init();
191 // IPv6
192 _SockAddr6->sin6_port = 0;
193 memcpy( &_SockAddr6->sin6_addr, ip, sizeof(in6_addr) );
195 // invalid IPv4
196 _SockAddr->sin_port = 0;
197 memset( &_SockAddr->sin_addr, 0, sizeof(in_addr) );
199 // get the host name to be displayed
200 if(hostname)
202 _HostName = hostname;
204 else
206 updateHostName();
209 _Valid = true;
214 * Update _HostName from _SockAddr current value
216 void CInetAddress::updateHostName()
218 char host[NI_MAXHOST];
220 // if unable to resolve DNS, returns an error and use IP address instead
221 sint status = 1;
223 // check if IPv4 is valid
224 if (_SockAddr->sin_addr.s_addr != 0)
226 // IPv4
227 status = getnameinfo((struct sockaddr *) _SockAddr, sizeof (sockaddr_in), host, NI_MAXHOST, NULL, 0, NI_NUMERICSERV | NI_NAMEREQD);
229 else if (!IN6_IS_ADDR_UNSPECIFIED(&_SockAddr6->sin6_addr))
231 // IPv6
232 status = getnameinfo((struct sockaddr *) _SockAddr6, sizeof (sockaddr_in6), host, NI_MAXHOST, NULL, 0, NI_NUMERICSERV | NI_NAMEREQD);
235 if ( status )
237 _HostName = ipAddress();
239 else
241 _HostName = string( host );
247 * Alternate constructor (calls setByName())
249 CInetAddress::CInetAddress( const std::string& hostName, uint16 port )
251 init();
252 setPort( port );
253 setByName( hostName );
258 * Alternate constructor (calls setNameAndPort())
260 CInetAddress::CInetAddress( const std::string& hostNameAndPort )
262 init();
263 setNameAndPort( hostNameAndPort );
268 * Copy constructor
270 CInetAddress::CInetAddress( const CInetAddress& other )
272 init();
273 _HostName = other._HostName;
274 memcpy( _SockAddr, other._SockAddr, sizeof( *_SockAddr ) );
275 memcpy( _SockAddr6, other._SockAddr6, sizeof( *_SockAddr6 ) );
276 _Valid = other._Valid;
281 * Assignment operator
283 CInetAddress& CInetAddress::operator=( const CInetAddress& other )
285 _HostName = other._HostName;
286 memcpy( _SockAddr, other._SockAddr, sizeof( *_SockAddr ) );
287 memcpy( _SockAddr6, other._SockAddr6, sizeof( *_SockAddr6 ) );
288 _Valid = other._Valid;
289 return *this;
294 * Comparison == operator
296 bool operator==( const CInetAddress& a1, const CInetAddress& a2 )
298 // Compares the sockaddr structure except the last 8 bytes equal to zero.
299 return ( memcmp( a1._SockAddr, a2._SockAddr, sizeof(sockaddr_in)-8 ) == 0 );
304 * Comparison < operator
306 bool operator<( const CInetAddress& a1, const CInetAddress& a2 )
308 if ( a1._SockAddr->sin_addr.s_addr == a2._SockAddr->sin_addr.s_addr )
310 return ( a1.port() < a2.port() );
312 else
314 return ( a1._SockAddr->sin_addr.s_addr < a2._SockAddr->sin_addr.s_addr );
320 * Constructor contents
322 void CInetAddress::init()
324 CSock::initNetwork();
326 _Valid = false;
328 // IPv4
329 _SockAddr = new sockaddr_in;
330 memset(_SockAddr, 0, sizeof(sockaddr_in));
331 _SockAddr->sin_family = AF_INET;
333 // IPv6
334 _SockAddr6 = new sockaddr_in6;
335 memset(_SockAddr6, 0, sizeof(sockaddr_in6));
336 _SockAddr6->sin6_family = AF_INET6;
341 * Destructor
343 CInetAddress::~CInetAddress()
345 delete _SockAddr;
346 delete _SockAddr6;
347 // _Valid = false;
351 * Sets hostname and port (ex: www.nevrax.com:80)
353 void CInetAddress::setNameAndPort( const std::string& hostNameAndPort )
355 string::size_type pos = hostNameAndPort.find_first_of (':');
356 if (pos != string::npos)
358 uint16 port;
359 fromString(hostNameAndPort.substr(pos + 1), port);
360 setPort( port );
362 else
364 setPort( 0 );
367 // if pos == -1, it will copy all the string
368 setByName( hostNameAndPort.substr (0, pos) );
373 * Resolves a name
375 CInetAddress& CInetAddress::setByName(const std::string& hostName)
377 // invalid IPv4
378 memset(&_SockAddr->sin_addr, 0, sizeof(in_addr));
380 // invalid IPv6
381 memset(&_SockAddr6->sin6_addr, 0, sizeof(in6_addr));
383 // Try to convert directly for addresses such as a.b.c.d and a:b:c:d:e:f:g:h
384 in_addr ipv4;
385 sint res = inet_pton(AF_INET, hostName.c_str(), &ipv4);
387 if (res == 1)
389 // hostname is a valid IPv4
390 memcpy(&_SockAddr->sin_addr, &ipv4, sizeof(in_addr));
392 else
394 in6_addr ipv6;
395 res = inet_pton(AF_INET6, hostName.c_str(), &ipv6);
397 if (res == 1)
399 // hostname is a valid IPv6
400 memcpy(&_SockAddr6->sin6_addr, &ipv6, sizeof(in6_addr));
404 if (res == 1)
406 // use IPv4 or IPv6 as hostname
407 _HostName = hostName;
409 else
411 // Otherwise use the traditional DNS look-up
412 struct addrinfo hints;
413 memset(&hints, 0, sizeof(hints));
414 hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version
415 hints.ai_socktype = SOCK_STREAM;
417 struct addrinfo *res = NULL;
418 sint status = getaddrinfo(hostName.c_str(), NULL, &hints, &res);
420 if (status)
422 _Valid = false;
423 LNETL0_DEBUG( "LNETL0: Network error: resolution of hostname '%s' failed: %s", hostName.c_str(), gai_strerror(status) );
424 // return *this;
425 throw ESocket( (string("Hostname resolution failed for ")+hostName).c_str() );
428 // hostname is valid, use it
429 _HostName = hostName;
431 struct addrinfo *p = res;
433 // process all addresses
434 while (p != NULL)
436 // check address family
437 if (p->ai_family == AF_INET)
439 // ipv4
440 struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
442 memcpy( &_SockAddr->sin_addr, &ipv4->sin_addr, sizeof(in_addr) );
444 else if (p->ai_family == AF_INET6)
446 // ipv6
447 struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
449 memcpy( &_SockAddr6->sin6_addr, &ipv6->sin6_addr, sizeof(in6_addr) );
452 // process next address
453 p = p->ai_next;
456 // free the linked list
457 freeaddrinfo(res);
460 _Valid = true;
461 return *this;
466 * Sets port
468 void CInetAddress::setPort(uint16 port)
470 _SockAddr->sin_port = htons(port);
471 _SockAddr6->sin6_port = htons(port);
475 /* Sets internal socket address directly (contents is copied).
476 * It also retrieves the host name if CInetAddress::RetrieveNames is true.
478 void CInetAddress::setSockAddr( const sockaddr_in* saddr )
480 memcpy(_SockAddr, saddr, sizeof(*saddr) );
482 // invalid IPv6
483 memset(&_SockAddr6->sin6_addr, 0, sizeof(in6_addr));
485 // Get host name
486 // Warning: when it can't find it, it take more than 4 seconds
487 if ( CInetAddress::RetrieveNames )
489 updateHostName();
492 _Valid = true;
496 /* Sets internal socket address directly (contents is copied).
497 * It also retrieves the host name if CInetAddress::RetrieveNames is true.
499 void CInetAddress::setSockAddr6( const sockaddr_in6* saddr6 )
501 memcpy( _SockAddr6, saddr6, sizeof(*saddr6) );
503 // invalid IPv4
504 memset(&_SockAddr->sin_addr, 0, sizeof(in_addr));
506 // Get host name
507 // Warning: when it can't find it, it take more than 4 seconds
508 if ( CInetAddress::RetrieveNames )
510 updateHostName();
513 _Valid = true;
518 * Returns if object (address and port) is valid
520 bool CInetAddress::isValid() const
522 return ( _Valid && _SockAddr->sin_port!=0 ); // same as ntohs(0)
527 * Returns internal IPv4 socket address (read only)
529 const sockaddr_in *CInetAddress::sockAddr() const
531 return _SockAddr;
536 * Returns internal IPv6 socket address (read only)
538 const sockaddr_in6 *CInetAddress::sockAddr6() const
540 return _SockAddr6;
545 * Returns internal IP address
547 uint32 CInetAddress::internalIPAddress() const
549 return _SockAddr->sin_addr.s_addr;
552 uint32 CInetAddress::internalNetAddress() const
554 uint32 ip = internalIPAddress();
555 if ((ip&0x00000080) == 0)
557 // A class
558 return ip & 0x000000FF;
560 else if ((ip&0x00000040) == 0)
562 // B class
563 return ip & 0x0000FFFF;
565 else if ((ip&0x00000020) == 0)
567 // C class
568 return ip & 0x00FFFFFF;
570 else if ((ip&0x00000010) == 0)
572 // D class
573 return ip & 0xFFFFFFFF;
575 else
577 return ip;
585 * Returns readable IP address. (ex: "195.68.21.195")
587 string CInetAddress::ipAddress() const
589 // longer size is IPv6
590 char straddr[INET6_ADDRSTRLEN];
591 const char *name = inet_ntop(AF_INET, &_SockAddr->sin_addr, straddr, INET_ADDRSTRLEN);
593 // IPv4 is invalid, return IPv6
594 if (name == NULL || strcmp(name, "0.0.0.0") == 0) name = inet_ntop(AF_INET6, &_SockAddr6->sin6_addr, straddr, INET6_ADDRSTRLEN);
596 return name ? string (name) : "";
601 * Returns host name. (ex: "www.nevrax.org")
603 const string& CInetAddress::hostName() const
605 return _HostName;
610 * Returns port
612 uint16 CInetAddress::port() const
614 return ntohs( _SockAddr->sin_port );
619 * Returns hostname and port as a string. (ex: "www.nevrax.org:80 (195.68.21.195)")
621 std::string CInetAddress::asString() const
623 return hostName() + ":" + NLMISC::toString(port()) + " (" + ipAddress() + ")";
628 * Returns IP address and port as a string. (ex: "195.68.21.195:80")
630 std::string CInetAddress::asIPString() const
632 return ipAddress() + ":" + NLMISC::toString(port());
637 * Serialize
639 void CInetAddress::serial( NLMISC::IStream& s )
641 NLMISC::CMemStream *ms = dynamic_cast<NLMISC::CMemStream*>(&s);
642 if ( ms && ms->stringMode() )
644 // String stream
645 string addrs;
646 if ( ms->isReading() )
648 ms->serial( addrs );
649 setNameAndPort( addrs );
651 else
653 addrs = asIPString();
654 ms->serial( addrs );
656 s.serial( _Valid );
658 else
660 // Binary stream
661 s.serialBuffer( (uint8*)_SockAddr, sizeof(*_SockAddr) ); // this is possible only because the contents of _SockAddr is platform-independant !
662 s.serial( _Valid );
664 if(_Valid)
666 // retreive the fullname
667 setSockAddr (_SockAddr);
675 * Creates a CInetAddress object with local host address, port=0
677 CInetAddress CInetAddress::localHost()
679 const uint maxlength = 80;
680 char localhost [maxlength];
681 if ( gethostname( localhost, maxlength ) != 0 )
682 throw ESocket( "Unable to get local hostname" );
683 CInetAddress localaddr = CInetAddress( string(localhost) );
685 if ( localaddr.ipAddress() == "127.0.0.1" )
687 nlwarning ("LNETL0: No network card detected! using localhost (127.0.0.1)");
690 return localaddr;
694 /* Returns the list of the local host addresses (with port=0)
695 * (especially useful if the host is multihomed)
697 std::vector<CInetAddress> CInetAddress::localAddresses()
699 // 1. Get local host name
700 const uint maxlength = 80;
701 char localhost [maxlength];
702 if ( gethostname( localhost, maxlength ) == SOCKET_ERROR )
704 throw ESocket( "Unable to get local hostname" );
707 // 2. Get address list
708 vector<CInetAddress> vect;
710 struct addrinfo hints;
711 memset(&hints, 0, sizeof(hints));
712 hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version
713 hints.ai_socktype = SOCK_STREAM;
715 struct addrinfo *res = NULL;
716 sint status = getaddrinfo(localhost, NULL, &hints, &res);
718 if (status)
720 // will come here if the local hostname (/etc/hostname in Linux) is not the real name
721 throw ESocket( toString("Hostname resolution failed for %s", localhost).c_str() );
724 struct addrinfo *p = res;
726 // for loopback ipv4
727 bool IPv4LoopbackAdded = false;
729 // for loopback ipv6
730 bool IPv6LoopbackAdded = false;
732 // process all addresses
733 while (p != NULL)
735 // check address family
736 if (p->ai_family == AF_INET)
738 // loopback ipv4
739 if (!IPv4LoopbackAdded)
741 // add loopback address only once
742 struct in_addr psin_addrIPv4;
743 psin_addrIPv4.s_addr = htonl(INADDR_LOOPBACK);
744 vect.push_back(CInetAddress(&psin_addrIPv4, localhost));
746 IPv4LoopbackAdded = true;
749 struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
751 vect.push_back( CInetAddress( &ipv4->sin_addr, localhost ) );
754 else if (p->ai_family == AF_INET6)
756 // loopback ipv6
757 if (!IPv6LoopbackAdded)
759 // add loopback address only once
760 struct in6_addr psin_addrIPv6 = IN6ADDR_LOOPBACK_INIT;
761 vect.push_back(CInetAddress(&psin_addrIPv6, localhost));
763 IPv6LoopbackAdded = true;
766 struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
768 vect.push_back( CInetAddress( &ipv6->sin6_addr, localhost ) );
771 // process next address
772 p = p->ai_next;
775 // free the linked list
776 freeaddrinfo(res);
778 if(vect.empty())
780 throw ESocket(toString("No network card detected for %s", localhost).c_str() );
783 return vect;
786 bool CInetAddress::is127001 () const
788 return (internalIPAddress () == htonl(0x7F000001));
791 bool CInetAddress::isLoopbackIPAddress () const
793 std::string sIPAddress = ipAddress();
795 return (sIPAddress.compare("::") == 0) ||
796 (sIPAddress.compare("::1") == 0) ||
797 (sIPAddress.compare("127.0.0.1") == 0) ||
798 (sIPAddress.compare("0:0:0:0:0:0:0:1") == 0);
802 std::string vectorCInetAddressToString(const std::vector<CInetAddress> &addrs)
804 string str;
806 for (uint i = 0; i < addrs.size(); i++)
808 if (i != 0)
809 str += " ";
810 str += addrs[i].asString().c_str ();
812 return str;
815 uint32 stringToInternalIPAddress (const std::string &addr)
817 return inet_addr( addr.c_str() );
820 std::string internalIPAddressToString (uint32 addr)
822 string res;
823 res = toString((addr)&0xFF);
824 res += ".";
825 res += toString((addr>>8)&0xFF);
826 res += ".";
827 res += toString((addr>>16)&0xFF);
828 res += ".";
829 res += toString((addr>>24)&0xFF);
830 return res;
834 } // NLNET