#define GAIH_EAI ~(GAIH_OKIFUNSPEC)
#ifndef UNIX_PATH_MAX
-#define UNIX_PATH_MAX 108
+# define UNIX_PATH_MAX 108
#endif
struct gaih_service
if (! tp->name[0])
{
if (req->ai_socktype)
- return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE);
+ return GAIH_OKIFUNSPEC | -EAI_SOCKTYPE;
else
- return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
+ return GAIH_OKIFUNSPEC | -EAI_SERVICE;
}
}
}
#endif /* 0 */
+
static int
gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
- const struct addrinfo *req, struct gaih_servtuple *st)
+ const struct addrinfo *req, struct gaih_servtuple *st)
{
struct servent *s;
size_t tmpbuflen = 1024;
int *errnop, int *h_errnop);
extern service_user *__nss_hosts_database attribute_hidden;
+
static int
gaih_inet (const char *name, const struct gaih_service *service,
const struct addrinfo *req, struct addrinfo **pai,
if (! tp->name[0])
{
if (req->ai_socktype)
- return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE);
+ return GAIH_OKIFUNSPEC | -EAI_SOCKTYPE;
else
- return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
+ return GAIH_OKIFUNSPEC | -EAI_SERVICE;
}
}
if (service != NULL)
{
if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
- return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
+ return GAIH_OKIFUNSPEC | -EAI_SERVICE;
if (service->num < 0)
{
pst = &(newp->next);
}
if (st == (struct gaih_servtuple *) &nullserv)
- return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
+ return GAIH_OKIFUNSPEC | -EAI_SERVICE;
}
}
else
}
/* We made requests but they turned out no data.
The name is known, though. */
- return (GAIH_OKIFUNSPEC | -EAI_NODATA);
+ return GAIH_OKIFUNSPEC | -EAI_NODATA;
}
goto process_list;
free (air);
if (at->family == AF_UNSPEC)
- return (GAIH_OKIFUNSPEC | -EAI_NONAME);
+ return GAIH_OKIFUNSPEC | -EAI_NONAME;
goto process_list;
}
/* We made requests but they turned out no data. The name
is known, though. */
- return (GAIH_OKIFUNSPEC | -EAI_NODATA);
+ return GAIH_OKIFUNSPEC | -EAI_NODATA;
}
}
process_list:
if (at->family == AF_UNSPEC)
- return (GAIH_OKIFUNSPEC | -EAI_NONAME);
+ return GAIH_OKIFUNSPEC | -EAI_NONAME;
}
else
{
struct sockaddr_storage source_addr;
uint8_t source_addr_len;
bool got_source_addr;
+ uint8_t source_addr_flags;
};
}
- /* Rule 3: Avoid deprecated addresses.
- That's something only the kernel could decide. */
+ /* Rule 3: Avoid deprecated addresses. */
+ if (a1->got_source_addr)
+ {
+ if (!(a1->source_addr_flags & in6ai_deprecated)
+ && (a2->source_addr_flags & in6ai_deprecated))
+ return -1;
+ if ((a1->source_addr_flags & in6ai_deprecated)
+ && !(a2->source_addr_flags & in6ai_deprecated))
+ return 1;
+ }
/* Rule 4: Prefer home addresses.
Another thing only the kernel can decide. */
return 1;
- /* Rule 7: Prefer native transport.
- XXX How to recognize tunnels? */
+ /* Rule 7: Prefer native transport. */
+ if (a1->got_source_addr)
+ {
+ if (!(a1->source_addr_flags & in6ai_temporary)
+ && (a1->source_addr_flags & in6ai_temporary))
+ return -1;
+ if ((a1->source_addr_flags & in6ai_temporary)
+ && !(a1->source_addr_flags & in6ai_temporary))
+ return -1;
+
+ /* XXX Do we need to check anything beside temporary addresses? */
+ }
/* Rule 8: Prefer smaller scope. */
}
+static int
+in6aicmp (const void *p1, const void *p2)
+{
+ struct in6addrinfo *a1 = (struct in6addrinfo *) p1;
+ struct in6addrinfo *a2 = (struct in6addrinfo *) p2;
+
+ return memcmp (a1->addr, a2->addr, sizeof (a1->addr));
+}
+
+
int
getaddrinfo (const char *name, const char *service,
const struct addrinfo *hints, struct addrinfo **pai)
if ((hints->ai_flags & AI_CANONNAME) && name == NULL)
return EAI_BADFLAGS;
+ struct in6addrinfo *in6ai;
+ size_t in6ailen;
+ bool seen_ipv4 = false;
+ bool seen_ipv6 = false;
+ /* We might need information about what kind of interfaces are available.
+ But even if AI_ADDRCONFIG is not used, if the user requested IPv6
+ addresses we have to know whether an address is deprecated or
+ temporary. */
+ if ((hints->ai_flags & AI_ADDRCONFIG) || hints->ai_family == PF_UNSPEC
+ || hints->ai_family == PF_INET6)
+ /* Determine whether we have IPv4 or IPv6 interfaces or both. We
+ cannot cache the results since new interfaces could be added at
+ any time. */
+ __check_pf (&seen_ipv4, &seen_ipv6, &in6ai, &in6ailen);
+
if (hints->ai_flags & AI_ADDRCONFIG)
{
- /* Determine whether we have IPv4 or IPv6 interfaces or both.
- We cannot cache the results since new interfaces could be
- added at any time. */
- bool seen_ipv4;
- bool seen_ipv6;
- __check_pf (&seen_ipv4, &seen_ipv6);
-
/* Now make a decision on what we return, if anything. */
if (hints->ai_family == PF_UNSPEC && (seen_ipv4 || seen_ipv6))
{
}
else if ((hints->ai_family == PF_INET && ! seen_ipv4)
|| (hints->ai_family == PF_INET6 && ! seen_ipv6))
- /* We cannot possibly return a valid answer. */
- return EAI_NONAME;
+ {
+ /* We cannot possibly return a valid answer. */
+ free (in6ai);
+ return EAI_NONAME;
+ }
}
if (service && service[0])
if (*c != '\0')
{
if (hints->ai_flags & AI_NUMERICSERV)
- return EAI_NONAME;
+ {
+ free (in6ai);
+ return EAI_NONAME;
+ }
gaih_service.num = -1;
}
}
freeaddrinfo (p);
+ free (in6ai);
return -(i & GAIH_EAI);
}
}
if (j == 0)
- return EAI_FAMILY;
+ {
+ free (in6ai);
+ return EAI_FAMILY;
+ }
if (naddrs > 1)
{
struct addrinfo *last = NULL;
char *canonname = NULL;
+ /* If we have information about deprecated and temporary address
+ sort the array now. */
+ if (in6ai != NULL)
+ qsort (in6ai, in6ailen, sizeof (*in6ai), in6aicmp);
+
for (i = 0, q = p; q != NULL; ++i, last = q, q = q->ai_next)
{
results[i].dest_addr = q;
results[i - 1].source_addr_len);
results[i].source_addr_len = results[i - 1].source_addr_len;
results[i].got_source_addr = results[i - 1].got_source_addr;
+ results[i].source_addr_flags = results[i - 1].source_addr_flags;
}
else
{
+ results[i].source_addr_flags = 0;
+
/* We overwrite the type with SOCK_DGRAM since we do not
want connect() to connect to the other side. If we
cannot determine the source address remember this
{
results[i].source_addr_len = sl;
results[i].got_source_addr = true;
+
+ if (q->ai_family == PF_INET6 && in6ai != NULL)
+ {
+ /* See whether the address is the list of deprecated
+ or temporary addresses. */
+ struct in6addrinfo tmp;
+ memcpy (tmp.addr, q->ai_addr, IN6ADDRSZ);
+
+ struct in6addrinfo *found
+ = bsearch (&tmp, in6ai, in6ailen, sizeof (*in6ai),
+ in6aicmp);
+ if (found != NULL)
+ results[i].source_addr_flags = found->flags;
+ }
}
else
/* Just make sure that if we have to process the same
p->ai_canonname = canonname;
}
+ free (in6ai);
+
if (p)
{
*pai = p;
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
+#include <not-cancel.h>
#include <kernel-features.h>
+#ifndef IFA_F_TEMPORARY
+# define IFA_F_TEMPORARY IFA_F_SECONDARY
+#endif
+
+
static int
-make_request (int fd, pid_t pid, bool *seen_ipv4, bool *seen_ipv6)
+make_request (int fd, pid_t pid, bool *seen_ipv4, bool *seen_ipv6,
+ struct in6addrinfo **in6ai, size_t *in6ailen)
{
struct
{
bool done = false;
char buf[4096];
struct iovec iov = { buf, sizeof (buf) };
+ struct in6ailist
+ {
+ struct in6addrinfo info;
+ struct in6ailist *next;
+ } *in6ailist = NULL;
+ size_t in6ailistlen = 0;
do
{
break;
case AF_INET6:
*seen_ipv6 = true;
+
+ if (ifam->ifa_flags & (IFA_F_DEPRECATED | IFA_F_TEMPORARY))
+ {
+ struct rtattr *rta = IFA_RTA (ifam);
+ size_t len = (nlmh->nlmsg_len
+ - NLMSG_LENGTH (sizeof (*ifam)));
+ void *local = NULL;
+ void *address = NULL;
+ while (RTA_OK (rta, len))
+ {
+ switch (rta->rta_type)
+ {
+ case IFA_LOCAL:
+ local = RTA_DATA (rta);
+ break;
+
+ case IFA_ADDRESS:
+ address = RTA_DATA (ta);
+ break;
+ }
+
+ rta = RTA_NEXT (rta, len);
+ }
+
+ struct in6ailist *newp = alloca (sizeof (*newp));
+ newp->info.flags = (((ifam->ifa_flags & IFA_F_DEPRECATED)
+ ? in6ai_deprecated : 0)
+ | ((ifam->ifa_flags
+ & IFA_F_TEMPORARY)
+ ? in6ai_temporary : 0));
+ memcpy (newp->info.addr, address ?: local,
+ sizeof (newp->info.addr));
+ newp->next = in6ailist;
+ in6ailsit = newp;
+ ++in6ailistlen;
+ }
break;
default:
/* Ignore. */
else if (nlmh->nlmsg_type == NLMSG_DONE)
/* We found the end, leave the loop. */
done = true;
- else ;
}
}
while (! done);
- __close (fd);
+ close_not_cancel_no_status (fd);
+
+ if (in6ailist != NULL)
+ {
+ *in6ai = malloc (in6ailistlen * sizeof (**in6ai));
+ if (*in6ai == NULL)
+ return -1;
+
+ *in6ailen = in6ailistlen;
+
+ do
+ {
+ (*in6ai)[--in6ailistlen] = in6ailist->info;
+ in6ailist = in6ailist->next;
+ }
+ while (in6ailist != NULL);
+ }
return 0;
}
void
attribute_hidden
-__check_pf (bool *seen_ipv4, bool *seen_ipv6)
+__check_pf (bool *seen_ipv4, bool *seen_ipv6,
+ struct in6addrinfo **in6ai, size_t *in6ailen)
{
+ *in6ai = NULL;
+ *in6ailen = 0;
+
if (! __no_netlink_support)
{
int fd = __socket (PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (fd >= 0
&& __bind (fd, (struct sockaddr *) &nladdr, sizeof (nladdr)) == 0
&& __getsockname (fd, (struct sockaddr *) &nladdr, &addr_len) == 0
- && make_request (fd, nladdr.nl_pid, seen_ipv4, seen_ipv6) == 0)
+ && make_request (fd, nladdr.nl_pid, seen_ipv4, seen_ipv6,
+ in6ai, in6ailen) == 0)
/* It worked. */
return;
return;
}
- *seen_ipv4 = false;
- *seen_ipv6 = false;
-
struct ifaddrs *runp;
for (runp = ifa; runp != NULL; runp = runp->ifa_next)
if (runp->ifa_addr->sa_family == PF_INET)