summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source3/lib/interface.c117
-rw-r--r--source3/lib/interfaces.c117
-rw-r--r--source3/lib/util_sock.c6
-rw-r--r--source3/libsmb/namequery.c26
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.
**************************************************************************/
@@ -326,100 +349,6 @@ static void add_interface(const struct iface_struct *ifs)
}
/****************************************************************************
- 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.
This handles the following different forms:
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
@@ -88,6 +88,104 @@
#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);
}