2008-10-17 14:26:46 +04:00
/*
Unix SMB / CIFS implementation .
Samba utility functions
Copyright ( C ) Jelmer Vernooij < jelmer @ samba . org > 2008
Copyright ( C ) Andrew Tridgell 1992 - 1998
2009-09-15 18:42:54 +04:00
Copyright ( C ) Jeremy Allison 1992 - 2007
2008-10-17 14:26:46 +04:00
Copyright ( C ) Simo Sorce 2001
Copyright ( C ) Jim McDonough ( jmcd @ us . ibm . com ) 2003.
Copyright ( C ) James J Myers 2003
2009-09-15 18:42:54 +04:00
Copyright ( C ) Tim Potter 2000 - 2001
2008-10-17 14:26:46 +04:00
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include "includes.h"
2008-10-18 17:00:18 +04:00
# include "system/network.h"
# include "system/locale.h"
# include "system/filesys.h"
2008-10-23 22:41:15 +04:00
# undef strcasecmp
2008-10-17 14:26:46 +04:00
2009-09-15 18:42:54 +04:00
/*******************************************************************
Set an address to INADDR_ANY .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void zero_sockaddr ( struct sockaddr_storage * pss )
{
memset ( pss , ' \0 ' , sizeof ( * pss ) ) ;
/* Ensure we're at least a valid sockaddr-storage. */
pss - > ss_family = AF_INET ;
}
2008-10-17 14:26:46 +04:00
/**
2008-10-23 22:41:15 +04:00
* Wrap getaddrinfo . . .
*/
bool interpret_string_addr_internal ( struct addrinfo * * ppres ,
const char * str , int flags )
2008-10-17 14:26:46 +04:00
{
2008-10-23 22:41:15 +04:00
int ret ;
struct addrinfo hints ;
2008-10-17 14:26:46 +04:00
2008-10-23 22:41:15 +04:00
memset ( & hints , ' \0 ' , sizeof ( hints ) ) ;
/* By default make sure it supports TCP. */
hints . ai_socktype = SOCK_STREAM ;
hints . ai_flags = flags ;
/* Linux man page on getaddinfo() says port will be
uninitialized when service string in NULL */
ret = getaddrinfo ( str , NULL ,
& hints ,
ppres ) ;
if ( ret ) {
DEBUG ( 3 , ( " interpret_string_addr_internal: getaddrinfo failed "
" for name %s [%s] \n " ,
str ,
gai_strerror ( ret ) ) ) ;
return false ;
2008-10-17 14:26:46 +04:00
}
2008-10-23 22:41:15 +04:00
return true ;
}
2009-09-15 18:42:54 +04:00
/*******************************************************************
Map a text hostname or IP address ( IPv4 or IPv6 ) into a
struct sockaddr_storage . Takes a flag which allows it to
prefer an IPv4 address ( needed for DC ' s ) .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static bool interpret_string_addr_pref ( struct sockaddr_storage * pss ,
const char * str ,
int flags ,
bool prefer_ipv4 )
{
struct addrinfo * res = NULL ;
# if defined(HAVE_IPV6)
char addr [ INET6_ADDRSTRLEN ] ;
unsigned int scope_id = 0 ;
if ( strchr_m ( str , ' : ' ) ) {
char * p = strchr_m ( str , ' % ' ) ;
/*
* Cope with link - local .
* This is IP : v6 : addr % ifname .
*/
if ( p & & ( p > str ) & & ( ( scope_id = if_nametoindex ( p + 1 ) ) ! = 0 ) ) {
strlcpy ( addr , str ,
MIN ( PTR_DIFF ( p , str ) + 1 ,
sizeof ( addr ) ) ) ;
str = addr ;
}
}
# endif
zero_sockaddr ( pss ) ;
if ( ! interpret_string_addr_internal ( & res , str , flags | AI_ADDRCONFIG ) ) {
return false ;
}
if ( ! res ) {
return false ;
}
if ( prefer_ipv4 ) {
struct addrinfo * p ;
for ( p = res ; p ; p = p - > ai_next ) {
if ( p - > ai_family = = AF_INET ) {
memcpy ( pss , p - > ai_addr , p - > ai_addrlen ) ;
break ;
}
}
if ( p = = NULL ) {
/* Copy the first sockaddr. */
memcpy ( pss , res - > ai_addr , res - > ai_addrlen ) ;
}
} else {
/* Copy the first sockaddr. */
memcpy ( pss , res - > ai_addr , res - > ai_addrlen ) ;
}
# if defined(HAVE_IPV6)
if ( pss - > ss_family = = AF_INET6 & & scope_id ) {
struct sockaddr_in6 * ps6 = ( struct sockaddr_in6 * ) pss ;
if ( IN6_IS_ADDR_LINKLOCAL ( & ps6 - > sin6_addr ) & &
ps6 - > sin6_scope_id = = 0 ) {
ps6 - > sin6_scope_id = scope_id ;
}
}
# endif
freeaddrinfo ( res ) ;
return true ;
}
/*******************************************************************
Map a text hostname or IP address ( IPv4 or IPv6 ) into a
struct sockaddr_storage . Address agnostic version .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
bool interpret_string_addr ( struct sockaddr_storage * pss ,
const char * str ,
int flags )
{
return interpret_string_addr_pref ( pss ,
str ,
flags ,
false ) ;
}
/*******************************************************************
Map a text hostname or IP address ( IPv4 or IPv6 ) into a
struct sockaddr_storage . Version that prefers IPv4 .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
bool interpret_string_addr_prefer_ipv4 ( struct sockaddr_storage * pss ,
const char * str ,
int flags )
{
return interpret_string_addr_pref ( pss ,
str ,
flags ,
true ) ;
}
2008-10-23 22:41:15 +04:00
/**
* Interpret an internet address or name into an IP address in 4 byte form .
* RETURNS IN NETWORK BYTE ORDER ( big endian ) .
*/
uint32_t interpret_addr ( const char * str )
{
uint32_t ret ;
2008-10-17 14:26:46 +04:00
2008-10-23 22:41:15 +04:00
/* If it's in the form of an IP address then
* get the lib to interpret it */
if ( is_ipaddress_v4 ( str ) ) {
struct in_addr dest ;
if ( inet_pton ( AF_INET , str , & dest ) < = 0 ) {
/* Error - this shouldn't happen ! */
DEBUG ( 0 , ( " interpret_addr: inet_pton failed "
" host %s \n " ,
str ) ) ;
return 0 ;
}
ret = dest . s_addr ; /* NETWORK BYTE ORDER ! */
2008-10-17 14:26:46 +04:00
} else {
2008-10-23 22:41:15 +04:00
/* Otherwise assume it's a network name of some sort and use
getadddrinfo . */
struct addrinfo * res = NULL ;
struct addrinfo * res_list = NULL ;
if ( ! interpret_string_addr_internal ( & res_list ,
str ,
AI_ADDRCONFIG ) ) {
DEBUG ( 3 , ( " interpret_addr: Unknown host. %s \n " , str ) ) ;
2008-10-17 14:26:46 +04:00
return 0 ;
}
2008-10-23 22:41:15 +04:00
/* Find the first IPv4 address. */
for ( res = res_list ; res ; res = res - > ai_next ) {
if ( res - > ai_family ! = AF_INET ) {
continue ;
}
if ( res - > ai_addr = = NULL ) {
continue ;
}
break ;
}
if ( res = = NULL ) {
DEBUG ( 3 , ( " interpret_addr: host address is "
" invalid for host %s \n " , str ) ) ;
if ( res_list ) {
freeaddrinfo ( res_list ) ;
}
2008-10-17 14:26:46 +04:00
return 0 ;
}
2008-10-23 22:41:15 +04:00
memcpy ( ( char * ) & ret ,
& ( ( struct sockaddr_in * ) res - > ai_addr ) - > sin_addr . s_addr ,
sizeof ( ret ) ) ;
if ( res_list ) {
freeaddrinfo ( res_list ) ;
}
2008-10-17 14:26:46 +04:00
}
2008-10-23 22:41:15 +04:00
/* This is so bogus - all callers need fixing... JRA. */
if ( ret = = ( uint32_t ) - 1 ) {
return 0 ;
}
2008-10-17 14:26:46 +04:00
2008-10-23 22:41:15 +04:00
return ret ;
2008-10-17 14:26:46 +04:00
}
/**
A convenient addition to interpret_addr ( ) .
* */
_PUBLIC_ struct in_addr interpret_addr2 ( const char * str )
{
struct in_addr ret ;
uint32_t a = interpret_addr ( str ) ;
ret . s_addr = a ;
return ret ;
}
/**
Check if an IP is the 0.0 .0 .0 .
* */
2008-10-23 22:41:15 +04:00
_PUBLIC_ bool is_zero_ip_v4 ( struct in_addr ip )
2008-10-17 14:26:46 +04:00
{
return ip . s_addr = = 0 ;
}
/**
Are two IPs on the same subnet ?
* */
2008-10-23 21:56:09 +04:00
_PUBLIC_ bool same_net_v4 ( struct in_addr ip1 , struct in_addr ip2 , struct in_addr mask )
2008-10-17 14:26:46 +04:00
{
uint32_t net1 , net2 , nmask ;
nmask = ntohl ( mask . s_addr ) ;
net1 = ntohl ( ip1 . s_addr ) ;
net2 = ntohl ( ip2 . s_addr ) ;
return ( ( net1 & nmask ) = = ( net2 & nmask ) ) ;
}
/**
2008-10-23 22:41:15 +04:00
* Return true if a string could be an IPv4 address .
*/
bool is_ipaddress_v4 ( const char * str )
{
int ret = - 1 ;
struct in_addr dest ;
ret = inet_pton ( AF_INET , str , & dest ) ;
if ( ret > 0 ) {
return true ;
}
return false ;
}
/**
* Return true if a string could be an IPv4 or IPv6 address .
*/
bool is_ipaddress ( const char * str )
{
# if defined(HAVE_IPV6)
int ret = - 1 ;
if ( strchr_m ( str , ' : ' ) ) {
char addr [ INET6_ADDRSTRLEN ] ;
struct in6_addr dest6 ;
const char * sp = str ;
char * p = strchr_m ( str , ' % ' ) ;
/*
* Cope with link - local .
* This is IP : v6 : addr % ifname .
*/
if ( p & & ( p > str ) & & ( if_nametoindex ( p + 1 ) ! = 0 ) ) {
strlcpy ( addr , str ,
MIN ( PTR_DIFF ( p , str ) + 1 ,
sizeof ( addr ) ) ) ;
sp = addr ;
}
ret = inet_pton ( AF_INET6 , sp , & dest6 ) ;
if ( ret > 0 ) {
return true ;
}
}
# endif
return is_ipaddress_v4 ( str ) ;
}
/**
* Is a sockaddr a broadcast address ?
*/
bool is_broadcast_addr ( const struct sockaddr * pss )
{
# if defined(HAVE_IPV6)
if ( pss - > sa_family = = AF_INET6 ) {
const struct in6_addr * sin6 =
& ( ( const struct sockaddr_in6 * ) pss ) - > sin6_addr ;
return IN6_IS_ADDR_MULTICAST ( sin6 ) ;
}
# endif
if ( pss - > sa_family = = AF_INET ) {
uint32_t addr =
ntohl ( ( ( const struct sockaddr_in * ) pss ) - > sin_addr . s_addr ) ;
return addr = = INADDR_BROADCAST ;
}
return false ;
}
/**
* Check if an IPv7 is 127.0 .0 .1
*/
bool is_loopback_ip_v4 ( struct in_addr ip )
{
struct in_addr a ;
a . s_addr = htonl ( INADDR_LOOPBACK ) ;
return ( ip . s_addr = = a . s_addr ) ;
}
2008-10-17 14:26:46 +04:00
2008-10-23 22:41:15 +04:00
/**
* Check if a struct sockaddr is the loopback address .
*/
bool is_loopback_addr ( const struct sockaddr * pss )
2008-10-17 14:26:46 +04:00
{
2008-10-23 22:41:15 +04:00
# if defined(HAVE_IPV6)
if ( pss - > sa_family = = AF_INET6 ) {
const struct in6_addr * pin6 =
& ( ( const struct sockaddr_in6 * ) pss ) - > sin6_addr ;
return IN6_IS_ADDR_LOOPBACK ( pin6 ) ;
}
# endif
if ( pss - > sa_family = = AF_INET ) {
const struct in_addr * pin = & ( ( const struct sockaddr_in * ) pss ) - > sin_addr ;
return is_loopback_ip_v4 ( * pin ) ;
}
return false ;
}
2008-10-17 14:26:46 +04:00
2008-10-23 22:41:15 +04:00
/**
* Check if a struct sockaddr has an unspecified address .
*/
bool is_zero_addr ( const struct sockaddr * pss )
{
# if defined(HAVE_IPV6)
if ( pss - > sa_family = = AF_INET6 ) {
const struct in6_addr * pin6 =
& ( ( const struct sockaddr_in6 * ) pss ) - > sin6_addr ;
return IN6_IS_ADDR_UNSPECIFIED ( pin6 ) ;
}
# endif
if ( pss - > sa_family = = AF_INET ) {
const struct in_addr * pin = & ( ( const struct sockaddr_in * ) pss ) - > sin_addr ;
return is_zero_ip_v4 ( * pin ) ;
}
return false ;
}
2008-10-17 14:26:46 +04:00
2008-10-23 22:41:15 +04:00
/**
* Set an IP to 0.0 .0 .0 .
*/
void zero_ip_v4 ( struct in_addr * ip )
{
memset ( ip , ' \0 ' , sizeof ( struct in_addr ) ) ;
}
2008-10-17 14:26:46 +04:00
2008-10-23 22:41:15 +04:00
/**
* Convert an IPv4 struct in_addr to a struct sockaddr_storage .
*/
void in_addr_to_sockaddr_storage ( struct sockaddr_storage * ss ,
struct in_addr ip )
{
struct sockaddr_in * sa = ( struct sockaddr_in * ) ss ;
memset ( ss , ' \0 ' , sizeof ( * ss ) ) ;
sa - > sin_family = AF_INET ;
sa - > sin_addr = ip ;
}
2008-10-17 14:26:46 +04:00
2008-10-23 22:41:15 +04:00
# if defined(HAVE_IPV6)
/**
* Convert an IPv6 struct in_addr to a struct sockaddr_storage .
*/
void in6_addr_to_sockaddr_storage ( struct sockaddr_storage * ss ,
struct in6_addr ip )
{
struct sockaddr_in6 * sa = ( struct sockaddr_in6 * ) ss ;
memset ( ss , ' \0 ' , sizeof ( * ss ) ) ;
sa - > sin6_family = AF_INET6 ;
sa - > sin6_addr = ip ;
2008-10-17 14:26:46 +04:00
}
2008-10-23 22:41:15 +04:00
# endif
2008-10-17 14:26:46 +04:00
2008-10-23 22:41:15 +04:00
/**
* Are two IPs on the same subnet ?
*/
bool same_net ( const struct sockaddr * ip1 ,
const struct sockaddr * ip2 ,
const struct sockaddr * mask )
{
if ( ip1 - > sa_family ! = ip2 - > sa_family ) {
/* Never on the same net. */
return false ;
}
2008-10-17 14:26:46 +04:00
2008-10-23 22:41:15 +04:00
# if defined(HAVE_IPV6)
if ( ip1 - > sa_family = = AF_INET6 ) {
struct sockaddr_in6 ip1_6 = * ( const struct sockaddr_in6 * ) ip1 ;
struct sockaddr_in6 ip2_6 = * ( const struct sockaddr_in6 * ) ip2 ;
struct sockaddr_in6 mask_6 = * ( const struct sockaddr_in6 * ) mask ;
char * p1 = ( char * ) & ip1_6 . sin6_addr ;
char * p2 = ( char * ) & ip2_6 . sin6_addr ;
char * m = ( char * ) & mask_6 . sin6_addr ;
int i ;
for ( i = 0 ; i < sizeof ( struct in6_addr ) ; i + + ) {
* p1 + + & = * m ;
* p2 + + & = * m ;
m + + ;
}
return ( memcmp ( & ip1_6 . sin6_addr ,
& ip2_6 . sin6_addr ,
sizeof ( struct in6_addr ) ) = = 0 ) ;
}
# endif
if ( ip1 - > sa_family = = AF_INET ) {
return same_net_v4 ( ( ( const struct sockaddr_in * ) ip1 ) - > sin_addr ,
( ( const struct sockaddr_in * ) ip2 ) - > sin_addr ,
( ( const struct sockaddr_in * ) mask ) - > sin_addr ) ;
}
return false ;
}
/**
* Are two sockaddr ' s the same family and address ? Ignore port etc .
*/
2008-12-03 10:29:57 +03:00
bool sockaddr_equal ( const struct sockaddr * ip1 ,
2008-10-23 22:41:15 +04:00
const struct sockaddr * ip2 )
{
if ( ip1 - > sa_family ! = ip2 - > sa_family ) {
/* Never the same. */
return false ;
}
# if defined(HAVE_IPV6)
if ( ip1 - > sa_family = = AF_INET6 ) {
return ( memcmp ( & ( ( const struct sockaddr_in6 * ) ip1 ) - > sin6_addr ,
& ( ( const struct sockaddr_in6 * ) ip2 ) - > sin6_addr ,
sizeof ( struct in6_addr ) ) = = 0 ) ;
}
# endif
if ( ip1 - > sa_family = = AF_INET ) {
return ( memcmp ( & ( ( const struct sockaddr_in * ) ip1 ) - > sin_addr ,
& ( ( const struct sockaddr_in * ) ip2 ) - > sin_addr ,
sizeof ( struct in_addr ) ) = = 0 ) ;
}
return false ;
}
/**
* Is an IP address the INADDR_ANY or in6addr_any value ?
*/
bool is_address_any ( const struct sockaddr * psa )
{
# if defined(HAVE_IPV6)
if ( psa - > sa_family = = AF_INET6 ) {
const struct sockaddr_in6 * si6 = ( const struct sockaddr_in6 * ) psa ;
if ( memcmp ( & in6addr_any ,
& si6 - > sin6_addr ,
sizeof ( in6addr_any ) ) = = 0 ) {
return true ;
}
return false ;
}
# endif
if ( psa - > sa_family = = AF_INET ) {
const struct sockaddr_in * si = ( const struct sockaddr_in * ) psa ;
if ( si - > sin_addr . s_addr = = INADDR_ANY ) {
return true ;
}
return false ;
}
return false ;
}
2008-10-23 23:08:13 +04:00
void set_sockaddr_port ( struct sockaddr * psa , uint16_t port )
{
# if defined(HAVE_IPV6)
if ( psa - > sa_family = = AF_INET6 ) {
( ( struct sockaddr_in6 * ) psa ) - > sin6_port = htons ( port ) ;
}
# endif
if ( psa - > sa_family = = AF_INET ) {
( ( struct sockaddr_in * ) psa ) - > sin_port = htons ( port ) ;
}
}