From 6128d116b3f09ce0b055d2df89b2f7282185782e Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Thu, 25 Oct 2007 18:28:36 -0700 Subject: Fix resolve name to resolve IPv6 addresses of link-local%ifaddr Jeremy. (This used to be commit e6609cab732d5cd5cc9a5ae50aee15147f2ec6ec) --- source3/lib/interface.c | 117 +++++++++------------------------------------ source3/lib/interfaces.c | 117 +++++++++++++++++++++++++++++++++++++++++++-- source3/lib/util_sock.c | 6 ++- source3/libsmb/namequery.c | 26 ++++++++++ 4 files changed, 166 insertions(+), 100 deletions(-) diff --git a/source3/lib/interface.c b/source3/lib/interface.c index 49bbceceb7..9d073bc08c 100644 --- a/source3/lib/interface.c +++ b/source3/lib/interface.c @@ -88,6 +88,29 @@ bool is_local_net(const struct sockaddr_storage *from) return false; } +#if defined(HAVE_IPV6) +void setup_linklocal_scope_id(struct sockaddr_storage *pss) +{ + struct interface *i; + for (i=local_interfaces;i;i=i->next) { + if (addr_equal(&i->ip,pss)) { + struct sockaddr_in6 *psa6 = + (struct sockaddr_in6 *)pss; + psa6->sin6_scope_id = if_nametoindex(i->name); + return; + } + } + for (i=local_interfaces;i;i=i->next) { + if (same_net(pss, &i->ip, &i->netmask)) { + struct sockaddr_in6 *psa6 = + (struct sockaddr_in6 *)pss; + psa6->sin6_scope_id = if_nametoindex(i->name); + return; + } + } +} +#endif + /**************************************************************************** Check if a packet is from a local (known) net. **************************************************************************/ @@ -325,100 +348,6 @@ static void add_interface(const struct iface_struct *ifs) &iface->netmask) )); } -/**************************************************************************** - Create a struct sockaddr_storage with the netmask bits set to 1. -****************************************************************************/ - -bool make_netmask(struct sockaddr_storage *pss_out, - const struct sockaddr_storage *pss_in, - unsigned long masklen) -{ - *pss_out = *pss_in; - /* Now apply masklen bits of mask. */ -#if defined(HAVE_IPV6) - if (pss_in->ss_family == AF_INET6) { - char *p = (char *)&((struct sockaddr_in6 *)pss_out)->sin6_addr; - unsigned int i; - - if (masklen > 128) { - return false; - } - for (i = 0; masklen >= 8; masklen -= 8, i++) { - *p++ = 0xff; - } - /* Deal with the partial byte. */ - *p++ &= (0xff & ~(0xff>>masklen)); - i++; - for (;i < sizeof(struct in6_addr); i++) { - *p++ = '\0'; - } - return true; - } -#endif - if (pss_in->ss_family == AF_INET) { - if (masklen > 32) { - return false; - } - ((struct sockaddr_in *)pss_out)->sin_addr.s_addr = - htonl(((0xFFFFFFFFL >> masklen) ^ 0xFFFFFFFFL)); - return true; - } - return false; -} - -/**************************************************************************** - Create a struct sockaddr_storage set to the broadcast or network adress from - an incoming sockaddr_storage. -****************************************************************************/ - -static void make_bcast_or_net(struct sockaddr_storage *pss_out, - const struct sockaddr_storage *pss_in, - const struct sockaddr_storage *nmask, - bool make_bcast) -{ - unsigned int i = 0, len = 0; - char *pmask = NULL; - char *p = NULL; - *pss_out = *pss_in; - - /* Set all zero netmask bits to 1. */ -#if defined(HAVE_IPV6) - if (pss_in->ss_family == AF_INET6) { - p = (char *)&((struct sockaddr_in6 *)pss_out)->sin6_addr; - pmask = (char *)&((struct sockaddr_in6 *)nmask)->sin6_addr; - len = 16; - } -#endif - if (pss_in->ss_family == AF_INET) { - p = (char *)&((struct sockaddr_in *)pss_out)->sin_addr; - pmask = (char *)&((struct sockaddr_in *)nmask)->sin_addr; - len = 4; - } - - for (i = 0; i < len; i++, p++, pmask++) { - if (make_bcast) { - *p = (*p & *pmask) | (*pmask ^ 0xff); - } else { - /* make_net */ - *p = (*p & *pmask); - } - } -} - -static void make_bcast(struct sockaddr_storage *pss_out, - const struct sockaddr_storage *pss_in, - const struct sockaddr_storage *nmask) -{ - make_bcast_or_net(pss_out, pss_in, nmask, true); -} - -static void make_net(struct sockaddr_storage *pss_out, - const struct sockaddr_storage *pss_in, - const struct sockaddr_storage *nmask) -{ - make_bcast_or_net(pss_out, pss_in, nmask, false); -} - /**************************************************************************** Interpret a single element from a interfaces= config line. diff --git a/source3/lib/interfaces.c b/source3/lib/interfaces.c index ee5d9dfb54..48fe249034 100644 --- a/source3/lib/interfaces.c +++ b/source3/lib/interfaces.c @@ -87,6 +87,104 @@ #include "interfaces.h" #include "lib/replace/replace.h" +/**************************************************************************** + Utility functions. +****************************************************************************/ + +/**************************************************************************** + Create a struct sockaddr_storage with the netmask bits set to 1. +****************************************************************************/ + +bool make_netmask(struct sockaddr_storage *pss_out, + const struct sockaddr_storage *pss_in, + unsigned long masklen) +{ + *pss_out = *pss_in; + /* Now apply masklen bits of mask. */ +#if defined(HAVE_IPV6) + if (pss_in->ss_family == AF_INET6) { + char *p = (char *)&((struct sockaddr_in6 *)pss_out)->sin6_addr; + unsigned int i; + + if (masklen > 128) { + return false; + } + for (i = 0; masklen >= 8; masklen -= 8, i++) { + *p++ = 0xff; + } + /* Deal with the partial byte. */ + *p++ &= (0xff & ~(0xff>>masklen)); + i++; + for (;i < sizeof(struct in6_addr); i++) { + *p++ = '\0'; + } + return true; + } +#endif + if (pss_in->ss_family == AF_INET) { + if (masklen > 32) { + return false; + } + ((struct sockaddr_in *)pss_out)->sin_addr.s_addr = + htonl(((0xFFFFFFFFL >> masklen) ^ 0xFFFFFFFFL)); + return true; + } + return false; +} + +/**************************************************************************** + Create a struct sockaddr_storage set to the broadcast or network adress from + an incoming sockaddr_storage. +****************************************************************************/ + +static void make_bcast_or_net(struct sockaddr_storage *pss_out, + const struct sockaddr_storage *pss_in, + const struct sockaddr_storage *nmask, + bool make_bcast) +{ + unsigned int i = 0, len = 0; + char *pmask = NULL; + char *p = NULL; + *pss_out = *pss_in; + + /* Set all zero netmask bits to 1. */ +#if defined(HAVE_IPV6) + if (pss_in->ss_family == AF_INET6) { + p = (char *)&((struct sockaddr_in6 *)pss_out)->sin6_addr; + pmask = (char *)&((struct sockaddr_in6 *)nmask)->sin6_addr; + len = 16; + } +#endif + if (pss_in->ss_family == AF_INET) { + p = (char *)&((struct sockaddr_in *)pss_out)->sin_addr; + pmask = (char *)&((struct sockaddr_in *)nmask)->sin_addr; + len = 4; + } + + for (i = 0; i < len; i++, p++, pmask++) { + if (make_bcast) { + *p = (*p & *pmask) | (*pmask ^ 0xff); + } else { + /* make_net */ + *p = (*p & *pmask); + } + } +} + +void make_bcast(struct sockaddr_storage *pss_out, + const struct sockaddr_storage *pss_in, + const struct sockaddr_storage *nmask) +{ + make_bcast_or_net(pss_out, pss_in, nmask, true); +} + +void make_net(struct sockaddr_storage *pss_out, + const struct sockaddr_storage *pss_in, + const struct sockaddr_storage *nmask) +{ + make_bcast_or_net(pss_out, pss_in, nmask, false); +} + /**************************************************************************** Try the "standard" getifaddrs/freeifaddrs interfaces. Also gets IPv6 interfaces. @@ -137,11 +235,20 @@ static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces) memcpy(&ifaces[total].ip, ifptr->ifa_addr, copy_size); memcpy(&ifaces[total].netmask, ifptr->ifa_netmask, copy_size); - if ((ifaces[total].flags & (IFF_BROADCAST|IFF_LOOPBACK)) && - ifptr->ifa_broadaddr) { - memcpy(&ifaces[total].bcast, - ifptr->ifa_broadaddr, - copy_size); + if (ifaces[total].flags & (IFF_BROADCAST|IFF_LOOPBACK)) { + if (ifptr->ifa_broadaddr) { + memcpy(&ifaces[total].bcast, + ifptr->ifa_broadaddr, + copy_size); + } else { + /* For some reason ifptr->ifa_broadaddr + * is null. Make one from ifa_addr and + * ifa_netmask. + */ + make_bcast(&ifaces[total].bcast, + &ifaces[total].ip, + &ifaces[total].netmask); + } } else if ((ifaces[total].flags & IFF_POINTOPOINT) && ifptr->ifa_dstaddr ) { memcpy(&ifaces[total].bcast, diff --git a/source3/lib/util_sock.c b/source3/lib/util_sock.c index 8a85f7a5c5..c30f21eeb7 100644 --- a/source3/lib/util_sock.c +++ b/source3/lib/util_sock.c @@ -64,7 +64,7 @@ bool is_ipaddress(const char *str) } /**************************************************************************** - Is a sockaddr_storage a broadcast address ? + Is a sockaddr_storage a broadcast address ? ****************************************************************************/ bool is_broadcast_addr(const struct sockaddr_storage *pss) @@ -1478,6 +1478,10 @@ int open_socket_out(int type, if (pss->ss_family == AF_INET6) { struct sockaddr_in6 *psa6 = (struct sockaddr_in6 *)&sock_out; psa6->sin6_port = htons(port); + if (psa6->sin6_scope_id == 0 && + IN6_IS_ADDR_LINKLOCAL(&psa6->sin6_addr)) { + setup_linklocal_scope_id(&sock_out); + } } #endif if (pss->ss_family == AF_INET) { diff --git a/source3/libsmb/namequery.c b/source3/libsmb/namequery.c index 34fe09b8c2..90e6be6a90 100644 --- a/source3/libsmb/namequery.c +++ b/source3/libsmb/namequery.c @@ -1594,6 +1594,32 @@ bool resolve_name(const char *name, char *sitename = NULL; int count = 0; +#if defined(HAVE_IPV6) + unsigned int if_idx = 0; + const char *p = strchr_m(name, '%'); + + if (p && (if_idx = if_nametoindex(p+1)) != 0) { + char *newname = SMB_STRDUP(name); + if (!newname) { + return false; + } + newname[PTR_DIFF(p,name)] = '\0'; + if (is_ipaddress(newname) && + interpret_string_addr(return_ss, + newname, AI_NUMERICHOST)) { + struct sockaddr_in6 *psa6 = + (struct sockaddr_in6 *)&return_ss; + if (psa6->sin6_scope_id == 0 && + IN6_IS_ADDR_LINKLOCAL(&psa6->sin6_addr)) { + psa6->sin6_scope_id = if_idx; + } + SAFE_FREE(newname); + return true; + } + SAFE_FREE(newname); + } +#endif + if (is_ipaddress(name)) { return interpret_string_addr(return_ss, name, AI_NUMERICHOST); } -- cgit