getaddrinfo with PF_UNSPEC and /etc/hosts
Ben Collins
bcollins@debian.org
Wed Nov 14 04:25:00 GMT 2001
On Thu, Nov 22, 2001 at 10:41:31PM +0200, Pekka Savola wrote:
> On 22 Nov 2001, Ulrich Drepper wrote:
>
> > Pekka Savola <pekkas@netcore.fi> writes:
> >
> > > It was pointed out by Rafal Wojtczuk on Owl development list that telnet
> > > that ships with Red Hat Linux 7.2 does not behave the way one would expect
> > > when resolving names using /etc/hosts. That is, if you have:
> > > [...]
> >
> > And you are unable to read the glibc documentation why? The BUGS file
> > clearly document this.
>
> Sorry, I didn't notice this.
>
> For more info, e.g.:
>
> http://sources.redhat.com/ml/libc-alpha/2000-11/msg00172.html
> http://bugs.gnu.org/cgi-bin/gnatsweb.pl?debug=&database=default&cmd=view+audit-trail&cmd=view&pr=1663
> http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=60743
>
> Debian appears to have a patch that AFAICS hacks around this
> (debian/patches/glibc22-getaddrinfo.dpatch), I haven't tested to see if
> this is the case.
The test has been tested for quite some time. IMO, it is a security
concern, but I was somewhat blown off when I last brought this up. The
"fix" is a real hack job, but it works, and has been tested since
2001-01-09 (2.2 + CVS), with no ill affects that I know of.
It's attached below for the interested.
2001-01-09 Ben Collins <bcollins@debian.org>
* sysdeps/posix/getaddrinfo.c(gethosts): Accept name to lookup,
so we can explicitly pass it to __gethostbyname2_r(). Also make
sure we decrement succeeded if that fails.
(tryname): New macro to do lookup handling, broken out of
gaih_inet().
(gaih_inet): Make use of tryname macro and special case a dotted
lookup to avoid searching IPv6 domain names on an PF_UNSPEC
lookup.
--
.----------=======-=-======-=========-----------=====------------=-=-----.
/ Ben Collins -- Debian GNU/Linux \
` bcollins@debian.org -- bcollins@openldap.org -- bcollins@linux.com '
`---=========------=======-------------=-=-----=-===-======-------=--=---'
-------------- next part --------------
--- glibc/sysdeps/posix/getaddrinfo.c~ 2000/11/18 08:30:02 1.34
+++ glibc/sysdeps/posix/getaddrinfo.c 2001/01/09 01:46:49
@@ -264,7 +264,7 @@
return 0;
}
-#define gethosts(_family, _type) \
+#define gethosts(_family, _type, _name) \
{ \
int i, herrno; \
size_t tmpbuflen; \
@@ -275,11 +275,12 @@
do { \
tmpbuflen *= 2; \
tmpbuf = __alloca (tmpbuflen); \
- rc = __gethostbyname2_r (name, _family, &th, tmpbuf, \
+ rc = __gethostbyname2_r (_name, _family, &th, tmpbuf, \
tmpbuflen, &h, &herrno); \
} while (rc == ERANGE && herrno == NETDB_INTERNAL); \
if (rc != 0) \
{ \
+ succeeded--; \
if (herrno == NETDB_INTERNAL) \
{ \
__set_h_errno (herrno); \
@@ -307,6 +308,26 @@
} \
}
+/* If we are looking for both IPv4 and IPv6 address we don't
+ want the lookup functions to automatically promote IPv4
+ addresses to IPv6 addresses. Currently this is decided
+ by setting the RES_USE_INET6 bit in _res.options. */
+#define tryname(__name) \
+ { \
+ succeeded = req->ai_family == AF_UNSPEC ? 2 : 1; \
+ if (req->ai_family == AF_UNSPEC) \
+ _res.options &= ~RES_USE_INET6; \
+ if (req->ai_family == AF_UNSPEC || \
+ req->ai_family == AF_INET6) \
+ gethosts (AF_INET6, struct in6_addr, __name); \
+ no_inet6_data = no_data; \
+ if (req->ai_family == AF_UNSPEC) \
+ _res.options = old_res_options; \
+ if (req->ai_family == AF_UNSPEC || \
+ req->ai_family == AF_INET) \
+ gethosts (AF_INET, struct in_addr, __name); \
+ }
+
static int
gaih_inet (const char *name, const struct gaih_service *service,
const struct addrinfo *req, struct addrinfo **pai)
@@ -316,6 +337,10 @@
struct gaih_addrtuple *at = NULL;
int rc;
+ /* Make sure we are initialized now */
+ if (!(_res.options & RES_INIT))
+ res_ninit(&_res);
+
if (req->ai_protocol || req->ai_socktype)
{
++tp;
@@ -489,34 +514,47 @@
struct gaih_addrtuple **pat = &at;
int no_data = 0;
int no_inet6_data;
+ int succeeded = -1;
int old_res_options = _res.options;
- /* If we are looking for both IPv4 and IPv6 address we don't
- want the lookup functions to automatically promote IPv4
- addresses to IPv6 addresses. Currently this is decided
- by setting the RES_USE_INET6 bit in _res.options. */
- if (req->ai_family == AF_UNSPEC)
- _res.options &= ~RES_USE_INET6;
-
- if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
- gethosts (AF_INET6, struct in6_addr);
- no_inet6_data = no_data;
+ /* If we have a dotted name, first try an absolute lookup. This
+ avoids problem where the resolver will try to search domains
+ even though we may have a fqdn, which is bad since ipv6 lookups
+ are performed first. */
+ if (strchr(name, '.') != NULL)
+ {
+ int namelen = strlen(name);
+ char *newname = __alloca(namelen + 2);
- if (req->ai_family == AF_UNSPEC)
- _res.options = old_res_options;
+ strcpy(newname, name);
+ newname[namelen] = '.';
+ newname[namelen+1] = '\0';
- if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
- gethosts (AF_INET, struct in_addr);
+ tryname(newname);
+ }
- if (no_data != 0 && no_inet6_data != 0)
+ /* If the previous didn't work, or we don't have a dotted name,
+ we try again like normal, letting the resolver manage domain
+ searching. */
+ if (succeeded <= 0)
{
- /* If both requests timed out report this. */
- if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN)
- return -EAI_AGAIN;
-
- /* We made requests but they turned out no data. The name
- is known, though. */
- return (GAIH_OKIFUNSPEC | -EAI_NODATA);
+ _res.options = old_res_options;
+
+ if (succeeded == 0) /* we've tried absolute, now try searches */
+ _res.ndots = 99;
+
+ tryname(name);
+
+ if (no_data != 0 && no_inet6_data != 0)
+ {
+ /* If both requests timed out report this. */
+ if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN)
+ return -EAI_AGAIN;
+
+ /* We made requests but they turned out no data. The name
+ is known, though. */
+ return (GAIH_OKIFUNSPEC | -EAI_NODATA);
+ }
}
}
More information about the Libc-alpha
mailing list