1
0
mirror of https://github.com/samba-team/samba.git synced 2025-11-23 20:23:50 +03:00

Add start of IPv6 implementation. Currently most of this is avoiding

IPv6 in winbindd, but moves most of the socket functions that were
wrongly in lib/util.c into lib/util_sock.c and provides generic
IPv4/6 independent versions of most things. Still lots of work
to do, but now I can see how I'll fix the access check code.
Nasty part that remains is the name resolution code which is
used to returning arrays of in_addr structs.
Jeremy.
This commit is contained in:
Jeremy Allison
2007-10-10 18:25:16 -07:00
parent 3f1ac564c0
commit 3f6bd0e1ec
46 changed files with 1378 additions and 677 deletions

View File

@@ -29,33 +29,353 @@ static int client_fd = -1;
static char client_ip_string[INET6_ADDRSTRLEN];
/****************************************************************************
Pritn out an IPv4 or IPv6 address from a struct sockaddr_storage.
Return true if a string could be a pure IPv4 address.
****************************************************************************/
bool is_ipaddress_v4(const char *str)
{
bool pure_address = true;
int i;
for (i=0; pure_address && str[i]; i++) {
if (!(isdigit((int)str[i]) || str[i] == '.')) {
pure_address = false;
}
}
/* Check that a pure number is not misinterpreted as an IP */
pure_address = pure_address && (strchr_m(str, '.') != NULL);
return pure_address;
}
/****************************************************************************
Interpret an internet address or name into an IP address in 4 byte form.
****************************************************************************/
uint32 interpret_addr(const char *str)
{
struct hostent *hp;
uint32 res;
if (strcmp(str,"0.0.0.0") == 0)
return(0);
if (strcmp(str,"255.255.255.255") == 0)
return(0xFFFFFFFF);
/* if it's in the form of an IP address then
* get the lib to interpret it */
if (is_ipaddress_v4(str)) {
res = inet_addr(str);
} else {
/* otherwise assume it's a network name of some sort and use
sys_gethostbyname */
if ((hp = sys_gethostbyname(str)) == 0) {
DEBUG(3,("sys_gethostbyname: Unknown host. %s\n",str));
return 0;
}
if(hp->h_addr == NULL) {
DEBUG(3,("sys_gethostbyname: host address is "
"invalid for host %s\n",str));
return 0;
}
putip((char *)&res,(char *)hp->h_addr);
}
if (res == (uint32)-1)
return(0);
return(res);
}
/*******************************************************************
A convenient addition to interpret_addr().
******************************************************************/
struct in_addr *interpret_addr2(const char *str)
{
static struct in_addr ret;
uint32 a = interpret_addr(str);
ret.s_addr = a;
return(&ret);
}
/*******************************************************************
Map a text hostname or IP address (IPv4 or IPv6) into a
struct sockaddr_storage.
******************************************************************/
bool interpret_string_addr(struct sockaddr_storage *pss, const char *str)
{
int ret;
struct addrinfo *res = NULL;
struct addrinfo hints;
memset(pss,'\0', sizeof(*pss));
memset(&hints, '\0', sizeof(hints));
/* By default make sure it supports TCP. */
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_ADDRCONFIG;
ret = getaddrinfo(str, NULL,
&hints,
&res);
if (ret) {
DEBUG(3,("interpret_string_addr: getaddrinfo failed for "
"name %s [%s]\n",
str,
gai_strerror(ret) ));
return false;
}
/* Copy the first sockaddr. */
memcpy(pss, res->ai_addr, res->ai_addrlen);
freeaddrinfo(res);
return true;
}
/*******************************************************************
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);
}
/*******************************************************************
Check if a struct sockaddr_storage is the loopback address.
******************************************************************/
bool is_loopback_addr(const struct sockaddr_storage *pss)
{
#if defined(AF_INET6)
if (pss->ss_family == AF_INET) {
struct in6_addr *pin6 = &((struct sockaddr_in6 *)pss)->sin6_addr;
return IN6_IS_ADDR_LOOPBACK(pin6);
}
#endif
if (pss->ss_family == AF_INET) {
struct in_addr *pin = &((struct sockaddr_in *)pss)->sin_addr;
return is_loopback_ip_v4(*pin);
}
return false;
}
/*******************************************************************
Check if an IPv4 is 0.0.0.0.
******************************************************************/
bool is_zero_ip_v4(struct in_addr ip)
{
uint32 a;
putip((char *)&a,(char *)&ip);
return(a == 0);
}
/*******************************************************************
Check if a struct sockaddr_storage has an unspecified address.
******************************************************************/
bool is_zero_addr(const struct sockaddr_storage *pss)
{
#if defined(AF_INET6)
if (pss->ss_family == AF_INET) {
struct in6_addr *pin6 = &((struct sockaddr_in6 *)pss)->sin6_addr;
return IN6_IS_ADDR_UNSPECIFIED(pin6);
}
#endif
if (pss->ss_family == AF_INET) {
struct in_addr *pin = &((struct sockaddr_in *)pss)->sin_addr;
return is_zero_ip_v4(*pin);
}
return false;
}
/*******************************************************************
Set an IP to 0.0.0.0.
******************************************************************/
void zero_ip_v4(struct in_addr *ip)
{
static bool init;
static struct in_addr ipzero;
if (!init) {
ipzero = *interpret_addr2("0.0.0.0");
init = true;
}
*ip = ipzero;
}
/*******************************************************************
Are two IPs on the same subnet - IPv4 version ?
********************************************************************/
bool same_net_v4(struct in_addr ip1,struct in_addr ip2,struct in_addr mask)
{
uint32 net1,net2,nmask;
nmask = ntohl(mask.s_addr);
net1 = ntohl(ip1.s_addr);
net2 = ntohl(ip2.s_addr);
return((net1 & nmask) == (net2 & nmask));
}
/*******************************************************************
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));
ss->ss_family = AF_INET;
sa->sin_addr = ip;
}
#ifdef AF_INET6
/*******************************************************************
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));
ss->ss_family = AF_INET6;
sa->sin6_addr = ip;
}
#endif
/*******************************************************************
Are two IPs on the same subnet?
********************************************************************/
bool same_net(const struct sockaddr_storage *ip1,
const struct sockaddr_storage *ip2,
const struct sockaddr_storage *mask)
{
if (ip1->ss_family != ip2->ss_family) {
/* Never on the same net. */
return false;
}
#ifdef AF_INET6
if (ip1->ss_family == AF_INET6) {
struct sockaddr_in6 ip1_6 = *(struct sockaddr_in6 *)ip1;
struct sockaddr_in6 ip2_6 = *(struct sockaddr_in6 *)ip2;
struct sockaddr_in6 mask_6 = *(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->ss_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_storage's the same family and address ? Ignore port etc.
********************************************************************/
bool addr_equal(const struct sockaddr_storage *ip1,
const struct sockaddr_storage *ip2)
{
if (ip1->ss_family != ip2->ss_family) {
/* Never the same. */
return false;
}
#ifdef AF_INET6
if (ip1->ss_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->ss_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_storage *psa)
{
#ifdef AF_INET6
if (psa->ss_family == AF_INET6) {
struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)psa;
if (memcmp(&in6addr_any,
&si6->sin6_addr,
sizeof(in6addr_any)) == 0) {
return true;
}
return false;
}
#endif
if (psa->ss_family == AF_INET) {
struct sockaddr_in *si = (struct sockaddr_in *)psa;
if (si->sin_addr.s_addr == INADDR_ANY) {
return true;
}
return false;
}
return false;
}
/****************************************************************************
Print out an IPv4 or IPv6 address from a struct sockaddr_storage.
****************************************************************************/
char *print_sockaddr(char *dest,
size_t destlen,
struct sockaddr_storage *psa)
const struct sockaddr_storage *psa,
socklen_t psalen)
{
if (destlen > 0) {
dest[0] = '\0';
}
#ifdef AF_INET6
if (psa->ss_family == AF_INET6) {
inet_ntop(AF_INET6,
&((struct sockaddr_in6 *)psa)->sin6_addr,
dest,
destlen);
}
#endif
if (psa->ss_family == AF_INET) {
inet_ntop(AF_INET,
&((struct sockaddr_in *)psa)->sin_addr,
dest,
destlen);
}
(void)getnameinfo((const struct sockaddr *)psa,
psalen,
dest, destlen,
NULL, 0,
NI_NUMERICHOST);
return dest;
}
/****************************************************************************
Set the global client_fd variable.
****************************************************************************/
void client_setfd(int fd)
{
client_fd = fd;
@@ -64,6 +384,10 @@ void client_setfd(int fd)
sizeof(client_ip_string)-1);
}
/****************************************************************************
Return a static string of an IP address (IPv4 or IPv6).
****************************************************************************/
static char *get_socket_addr(int fd)
{
struct sockaddr_storage sa;
@@ -87,9 +411,13 @@ static char *get_socket_addr(int fd)
return addr_buf;
}
return print_sockaddr(addr_buf, sizeof(addr_buf), &sa);
return print_sockaddr(addr_buf, sizeof(addr_buf), &sa, length);
}
/****************************************************************************
Return the port number we've bound to on a socket.
****************************************************************************/
static int get_socket_port(int fd)
{
struct sockaddr_storage sa;
@@ -118,7 +446,7 @@ static int get_socket_port(int fd)
char *client_name(void)
{
return get_peer_name(client_fd,False);
return get_peer_name(client_fd,false);
}
char *client_addr(void)
@@ -142,7 +470,7 @@ int smb_read_error = 0;
Determine if a file descriptor is in fact a socket.
****************************************************************************/
BOOL is_a_socket(int fd)
bool is_a_socket(int fd)
{
int v;
socklen_t l;
@@ -247,12 +575,12 @@ void set_socket_options(int fd, const char *options)
int ret=0,i;
int value = 1;
char *p;
BOOL got_value = False;
bool got_value = false;
if ((p = strchr_m(tok,'='))) {
*p = 0;
value = atoi(p+1);
got_value = True;
got_value = true;
}
for (i=0;socket_options[i].name;i++)
@@ -559,7 +887,7 @@ ssize_t write_data(int fd, const char *buffer, size_t N)
Send a keepalive packet (rfc1002).
****************************************************************************/
BOOL send_keepalive(int client)
bool send_keepalive(int client)
{
unsigned char buf[4];
@@ -583,7 +911,7 @@ static ssize_t read_smb_length_return_keepalive(int fd,
{
ssize_t len=0;
int msg_type;
BOOL ok = False;
bool ok = false;
while (!ok) {
if (timeout > 0) {
@@ -809,23 +1137,23 @@ static ssize_t receive_smb_raw_talloc(TALLOC_CTX *mem_ctx, int fd,
Checks the MAC on signed packets.
****************************************************************************/
BOOL receive_smb(int fd, char *buffer, unsigned int timeout)
bool receive_smb(int fd, char *buffer, unsigned int timeout)
{
if (receive_smb_raw(fd, buffer, timeout, 0) < 0) {
return False;
return false;
}
/* Check the incoming SMB signature. */
if (!srv_check_sign_mac(buffer, True)) {
if (!srv_check_sign_mac(buffer, true)) {
DEBUG(0, ("receive_smb: SMB Signature verification "
"failed on incoming packet!\n"));
if (smb_read_error == 0) {
smb_read_error = READ_BAD_SIG;
}
return False;
return false;
}
return True;
return true;
}
ssize_t receive_smb_talloc(TALLOC_CTX *mem_ctx, int fd, char **buffer,
@@ -840,7 +1168,7 @@ ssize_t receive_smb_talloc(TALLOC_CTX *mem_ctx, int fd, char **buffer,
}
/* Check the incoming SMB signature. */
if (!srv_check_sign_mac(*buffer, True)) {
if (!srv_check_sign_mac(*buffer, true)) {
DEBUG(0, ("receive_smb: SMB Signature verification failed on "
"incoming packet!\n"));
if (smb_read_error == 0) {
@@ -856,7 +1184,7 @@ ssize_t receive_smb_talloc(TALLOC_CTX *mem_ctx, int fd, char **buffer,
Send an smb to a fd.
****************************************************************************/
BOOL send_smb(int fd, char *buffer)
bool send_smb(int fd, char *buffer)
{
size_t len;
size_t nwritten=0;
@@ -872,12 +1200,12 @@ BOOL send_smb(int fd, char *buffer)
if (ret <= 0) {
DEBUG(0,("Error writing %d bytes to client. %d. (%s)\n",
(int)len,(int)ret, strerror(errno) ));
return False;
return false;
}
nwritten += ret;
}
return True;
return true;
}
/****************************************************************************
@@ -888,7 +1216,7 @@ int open_socket_in(int type,
int port,
int dlevel,
uint32 socket_addr,
BOOL rebind )
bool rebind )
{
struct sockaddr_in sock;
int res;
@@ -919,7 +1247,7 @@ int open_socket_in(int type,
if( DEBUGLVL( dlevel ) ) {
dbgtext( "open_socket_in(): setsockopt: " );
dbgtext( "SO_REUSEADDR = %s ",
val?"True":"False" );
val?"true":"false" );
dbgtext( "on port %d failed ", port );
dbgtext( "with error = %s\n", strerror(errno) );
}
@@ -930,7 +1258,7 @@ int open_socket_in(int type,
if( DEBUGLVL( dlevel ) ) {
dbgtext( "open_socket_in(): setsockopt: ");
dbgtext( "SO_REUSEPORT = %s ",
val?"True":"False" );
val?"true":"false" );
dbgtext( "on port %d failed ", port );
dbgtext( "with error = %s\n", strerror(errno) );
}
@@ -984,7 +1312,7 @@ int open_socket_out(int type, struct in_addr *addr, int port ,int timeout)
sock_out.sin_family = PF_INET;
/* set it non-blocking */
set_blocking(res,False);
set_blocking(res,false);
DEBUG(3,("Connecting to %s at port %d\n",inet_ntoa(*addr),port));
@@ -1029,7 +1357,7 @@ int open_socket_out(int type, struct in_addr *addr, int port ,int timeout)
}
/* set it blocking again */
set_blocking(res,True);
set_blocking(res,true);
return res;
}
@@ -1040,12 +1368,12 @@ int open_socket_out(int type, struct in_addr *addr, int port ,int timeout)
of DC's all of which are equivalent for our purposes.
**************************************************************************/
BOOL open_any_socket_out(struct sockaddr_in *addrs, int num_addrs,
bool open_any_socket_out(struct sockaddr_in *addrs, int num_addrs,
int timeout, int *fd_index, int *fd)
{
int i, resulting_index, res;
int *sockets;
BOOL good_connect;
bool good_connect;
fd_set r_fds, wr_fds;
struct timeval tv;
@@ -1058,7 +1386,7 @@ BOOL open_any_socket_out(struct sockaddr_in *addrs, int num_addrs,
sockets = SMB_MALLOC_ARRAY(int, num_addrs);
if (sockets == NULL)
return False;
return false;
resulting_index = -1;
@@ -1069,11 +1397,11 @@ BOOL open_any_socket_out(struct sockaddr_in *addrs, int num_addrs,
sockets[i] = socket(PF_INET, SOCK_STREAM, 0);
if (sockets[i] < 0)
goto done;
set_blocking(sockets[i], False);
set_blocking(sockets[i], false);
}
connect_again:
good_connect = False;
good_connect = false;
for (i=0; i<num_addrs; i++) {
@@ -1095,7 +1423,7 @@ BOOL open_any_socket_out(struct sockaddr_in *addrs, int num_addrs,
errno == EAGAIN || errno == EINTR) {
/* These are the error messages that something is
progressing. */
good_connect = True;
good_connect = true;
} else if (errno != 0) {
/* There was a direct error */
close(sockets[i]);
@@ -1180,7 +1508,7 @@ BOOL open_any_socket_out(struct sockaddr_in *addrs, int num_addrs,
if (resulting_index >= 0) {
*fd_index = resulting_index;
*fd = sockets[*fd_index];
set_blocking(*fd, True);
set_blocking(*fd, true);
}
free(sockets);
@@ -1223,7 +1551,7 @@ int open_udp_socket(const char *host, int port)
confirm a hostname lookup to prevent spoof attacks.
******************************************************************/
static BOOL matchname(char *remotehost,struct in_addr addr)
static bool matchname(char *remotehost,struct in_addr addr)
{
struct hostent *hp;
int i;
@@ -1231,7 +1559,7 @@ static BOOL matchname(char *remotehost,struct in_addr addr)
if ((hp = sys_gethostbyname(remotehost)) == 0) {
DEBUG(0,("sys_gethostbyname(%s): lookup failure.\n",
remotehost));
return False;
return false;
}
/*
@@ -1246,13 +1574,13 @@ static BOOL matchname(char *remotehost,struct in_addr addr)
&& !strequal(remotehost, "localhost")) {
DEBUG(0,("host name/name mismatch: %s != %s\n",
remotehost, hp->h_name));
return False;
return false;
}
/* Look up the host address in the address list we just got. */
for (i = 0; hp->h_addr_list[i]; i++) {
if (memcmp(hp->h_addr_list[i], (char *)&addr,sizeof(addr)) == 0)
return True;
return true;
}
/*
@@ -1263,14 +1591,14 @@ static BOOL matchname(char *remotehost,struct in_addr addr)
DEBUG(0,("host name/address mismatch: %s != %s\n",
inet_ntoa(addr), hp->h_name));
return False;
return false;
}
/*******************************************************************
Return the DNS name of the remote end of a socket.
******************************************************************/
char *get_peer_name(int fd, BOOL force_lookup)
char *get_peer_name(int fd, bool force_lookup)
{
static pstring name_buf;
pstring tmp_name;
@@ -1283,7 +1611,7 @@ char *get_peer_name(int fd, BOOL force_lookup)
situations won't work because many networks don't link dhcp
with dns. To avoid the delay we avoid the lookup if
possible */
if (!lp_hostname_lookups() && (force_lookup == False)) {
if (!lp_hostname_lookups() && (force_lookup == false)) {
return get_peer_addr(fd);
}
@@ -1450,3 +1778,92 @@ out_umask:
return -1;
#endif /* HAVE_UNIXSOCKET */
}
/************************************************************
Is this my name ? Needs fixing for IPv6.
************************************************************/
bool is_myname_or_ipaddr(const char *s)
{
fstring name, dnsname;
char *servername;
if ( !s )
return false;
/* santize the string from '\\name' */
fstrcpy( name, s );
servername = strrchr_m( name, '\\' );
if ( !servername )
servername = name;
else
servername++;
/* optimize for the common case */
if (strequal(servername, global_myname()))
return true;
/* check for an alias */
if (is_myname(servername))
return true;
/* check for loopback */
if (strequal(servername, "127.0.0.1"))
return true;
if (strequal(servername, "localhost"))
return true;
/* maybe it's my dns name */
if ( get_mydnsfullname( dnsname ) )
if ( strequal( servername, dnsname ) )
return true;
/* handle possible CNAME records */
if ( !is_ipaddress_v4( servername ) ) {
/* use DNS to resolve the name, but only the first address */
struct hostent *hp;
if (((hp = sys_gethostbyname(name)) != NULL) && (hp->h_addr != NULL)) {
struct in_addr return_ip;
putip( (char*)&return_ip, (char*)hp->h_addr );
fstrcpy( name, inet_ntoa( return_ip ) );
servername = name;
}
}
/* maybe its an IP address? */
if (is_ipaddress_v4(servername)) {
struct sockaddr_storage ss;
struct iface_struct nics[MAX_INTERFACES];
int i, n;
struct in_addr ip;
ip = *interpret_addr2(servername);
if (is_zero_ip_v4(ip) || is_loopback_ip_v4(ip)) {
return false;
}
in_addr_to_sockaddr_storage(&ss, ip);
n = get_interfaces(nics, MAX_INTERFACES);
for (i=0; i<n; i++) {
if (nics[i].ip.ss_family != AF_INET) {
continue;
}
if (addr_equal(&nics[i].ip, &ss)) {
return true;
}
}
}
/* no match */
return false;
}