1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
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.
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/>.
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"
28 # include <winsock2.h>
29 # include <ws2tcpip.h>
30 # include <ws2ipdef.h>
31 // for Windows 2000 compatibility
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)
55 *(struct in_addr
*)dst
= ((struct sockaddr_in
*)&ss
)->sin_addr
;
59 *(struct in6_addr
*)dst
= ((struct sockaddr_in6
*)&ss
)->sin6_addr
;
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
));
78 ((struct sockaddr_in
*)&ss
)->sin_addr
= *(struct in_addr
*)src
;
82 ((struct sockaddr_in6
*)&ss
)->sin6_addr
= *(struct in6_addr
*)src
;
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));
111 #elif defined NL_OS_UNIX
113 # include <sys/socket.h>
114 # include <arpa/inet.h>
115 # include <netinet/in.h>
117 # define WSAGetLastError() 0
118 # define SOCKET_ERROR -1
122 using namespace NLMISC
;
125 #define new DEBUG_NEW
129 # define NI_MAXHOST 1025
135 bool CInetAddress::RetrieveNames
= false;
141 CInetAddress::CInetAddress()
146 _SockAddr
->sin_port
= 0; // same as htons(0)
147 memset( &_SockAddr
->sin_addr
, 0, sizeof(in_addr
) ); // same as htonl(INADDR_ANY)
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
)
163 _SockAddr
->sin_port
= 0;
164 memcpy( &_SockAddr
->sin_addr
, ip
, sizeof(in_addr
) );
167 _SockAddr6
->sin6_port
= 0;
168 memset( &_SockAddr6
->sin6_addr
, 0, sizeof(in6_addr
) );
170 // get the host name to be displayed
173 _HostName
= hostname
;
185 * Constructor with IPv6 address, port=0
187 CInetAddress::CInetAddress( const in6_addr
*ip
, const char *hostname
)
192 _SockAddr6
->sin6_port
= 0;
193 memcpy( &_SockAddr6
->sin6_addr
, ip
, sizeof(in6_addr
) );
196 _SockAddr
->sin_port
= 0;
197 memset( &_SockAddr
->sin_addr
, 0, sizeof(in_addr
) );
199 // get the host name to be displayed
202 _HostName
= hostname
;
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
223 // check if IPv4 is valid
224 if (_SockAddr
->sin_addr
.s_addr
!= 0)
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
))
232 status
= getnameinfo((struct sockaddr
*) _SockAddr6
, sizeof (sockaddr_in6
), host
, NI_MAXHOST
, NULL
, 0, NI_NUMERICSERV
| NI_NAMEREQD
);
237 _HostName
= ipAddress();
241 _HostName
= string( host
);
247 * Alternate constructor (calls setByName())
249 CInetAddress::CInetAddress( const std::string
& hostName
, uint16 port
)
253 setByName( hostName
);
258 * Alternate constructor (calls setNameAndPort())
260 CInetAddress::CInetAddress( const std::string
& hostNameAndPort
)
263 setNameAndPort( hostNameAndPort
);
270 CInetAddress::CInetAddress( const CInetAddress
& other
)
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
;
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() );
314 return ( a1
._SockAddr
->sin_addr
.s_addr
< a2
._SockAddr
->sin_addr
.s_addr
);
320 * Constructor contents
322 void CInetAddress::init()
324 CSock::initNetwork();
329 _SockAddr
= new sockaddr_in
;
330 memset(_SockAddr
, 0, sizeof(sockaddr_in
));
331 _SockAddr
->sin_family
= AF_INET
;
334 _SockAddr6
= new sockaddr_in6
;
335 memset(_SockAddr6
, 0, sizeof(sockaddr_in6
));
336 _SockAddr6
->sin6_family
= AF_INET6
;
343 CInetAddress::~CInetAddress()
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
)
359 fromString(hostNameAndPort
.substr(pos
+ 1), port
);
367 // if pos == -1, it will copy all the string
368 setByName( hostNameAndPort
.substr (0, pos
) );
375 CInetAddress
& CInetAddress::setByName(const std::string
& hostName
)
378 memset(&_SockAddr
->sin_addr
, 0, sizeof(in_addr
));
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
385 sint res
= inet_pton(AF_INET
, hostName
.c_str(), &ipv4
);
389 // hostname is a valid IPv4
390 memcpy(&_SockAddr
->sin_addr
, &ipv4
, sizeof(in_addr
));
395 res
= inet_pton(AF_INET6
, hostName
.c_str(), &ipv6
);
399 // hostname is a valid IPv6
400 memcpy(&_SockAddr6
->sin6_addr
, &ipv6
, sizeof(in6_addr
));
406 // use IPv4 or IPv6 as hostname
407 _HostName
= hostName
;
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
);
423 LNETL0_DEBUG( "LNETL0: Network error: resolution of hostname '%s' failed: %s", hostName
.c_str(), gai_strerror(status
) );
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
436 // check address family
437 if (p
->ai_family
== AF_INET
)
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
)
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
456 // free the linked list
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
) );
483 memset(&_SockAddr6
->sin6_addr
, 0, sizeof(in6_addr
));
486 // Warning: when it can't find it, it take more than 4 seconds
487 if ( CInetAddress::RetrieveNames
)
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
) );
504 memset(&_SockAddr
->sin_addr
, 0, sizeof(in_addr
));
507 // Warning: when it can't find it, it take more than 4 seconds
508 if ( CInetAddress::RetrieveNames
)
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
536 * Returns internal IPv6 socket address (read only)
538 const sockaddr_in6
*CInetAddress::sockAddr6() const
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)
558 return ip
& 0x000000FF;
560 else if ((ip
&0x00000040) == 0)
563 return ip
& 0x0000FFFF;
565 else if ((ip
&0x00000020) == 0)
568 return ip
& 0x00FFFFFF;
570 else if ((ip
&0x00000010) == 0)
573 return ip
& 0xFFFFFFFF;
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
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());
639 void CInetAddress::serial( NLMISC::IStream
& s
)
641 NLMISC::CMemStream
*ms
= dynamic_cast<NLMISC::CMemStream
*>(&s
);
642 if ( ms
&& ms
->stringMode() )
646 if ( ms
->isReading() )
649 setNameAndPort( addrs
);
653 addrs
= asIPString();
661 s
.serialBuffer( (uint8
*)_SockAddr
, sizeof(*_SockAddr
) ); // this is possible only because the contents of _SockAddr is platform-independant !
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)");
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
);
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
;
727 bool IPv4LoopbackAdded
= false;
730 bool IPv6LoopbackAdded
= false;
732 // process all addresses
735 // check address family
736 if (p
->ai_family
== AF_INET
)
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
)
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
775 // free the linked list
780 throw ESocket(toString("No network card detected for %s", localhost
).c_str() );
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
)
806 for (uint i
= 0; i
< addrs
.size(); i
++)
810 str
+= addrs
[i
].asString().c_str ();
815 uint32
stringToInternalIPAddress (const std::string
&addr
)
817 return inet_addr( addr
.c_str() );
820 std::string
internalIPAddressToString (uint32 addr
)
823 res
= toString((addr
)&0xFF);
825 res
+= toString((addr
>>8)&0xFF);
827 res
+= toString((addr
>>16)&0xFF);
829 res
+= toString((addr
>>24)&0xFF);