From 8e54530b52fd256137740107e9fdf000f00a7a30 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 10 Oct 2007 18:25:16 -0700 Subject: 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 used to be commit 3f6bd0e1ec5cc6670f3d08f76fc2cd94c9cd1a08) --- source3/lib/util_sock.c | 515 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 466 insertions(+), 49 deletions(-) (limited to 'source3/lib/util_sock.c') diff --git a/source3/lib/util_sock.c b/source3/lib/util_sock.c index 1508ddfce3..7a1a05ec29 100644 --- a/source3/lib/util_sock.c +++ b/source3/lib/util_sock.c @@ -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. ****************************************************************************/ -char *print_sockaddr(char *dest, - size_t destlen, - struct sockaddr_storage *psa) +bool is_ipaddress_v4(const char *str) { - if (destlen > 0) { - dest[0] = '\0'; + 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) { - inet_ntop(AF_INET6, - &((struct sockaddr_in6 *)psa)->sin6_addr, - dest, - destlen); + 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) { - inet_ntop(AF_INET, - &((struct sockaddr_in *)psa)->sin_addr, - dest, - destlen); + 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, + const struct sockaddr_storage *psa, + socklen_t psalen) +{ + if (destlen > 0) { + dest[0] = '\0'; + } + (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= 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