Fix getaddrinfo with protocol unset on Solaris 9. Found by Dagobert Michelsen

Apparently when you call Solaris 9's getaddrinfo(), it likes to leave
ai_protocol unset in the result. This is no way to behave, if I'm
reading RFC3493 right.

This patch makes us check for a getaddrinfo() that's broken in this way,
and work around it by trying to infer socktype and protocol from one
another.

Partial bugfix for 2987542
This commit is contained in:
Nick Mathewson 2010-04-21 11:57:55 -04:00
parent c44de06c76
commit 2cf2a286e4

View File

@ -959,6 +959,10 @@ evutil_adjust_hints_for_addrconfig(struct evutil_addrinfo *hints)
} }
#ifdef USE_NATIVE_GETADDRINFO #ifdef USE_NATIVE_GETADDRINFO
static int need_numeric_port_hack_=0;
static int need_socktype_protocol_hack_=0;
static int tested_for_getaddrinfo_hacks=0;
/* Some older BSDs (like OpenBSD up to 4.6) used to believe that /* Some older BSDs (like OpenBSD up to 4.6) used to believe that
giving a numeric port without giving an ai_socktype was verboten. giving a numeric port without giving an ai_socktype was verboten.
We test for this so we can apply an appropriate workaround. If it We test for this so we can apply an appropriate workaround. If it
@ -973,17 +977,17 @@ evutil_adjust_hints_for_addrconfig(struct evutil_addrinfo *hints)
We test for this bug at runtime, since otherwise we can't have the We test for this bug at runtime, since otherwise we can't have the
same binary run on multiple BSD versions. same binary run on multiple BSD versions.
- Some versions of Solaris believe that it's nice to leave to protocol
field set to 0. We test for this so we can apply an appropriate
workaround.
*/ */
static int static void
need_numeric_port_hack(void) test_for_getaddrinfo_hacks(void)
{ {
static int tested=0;
static int need_hack=0;
int r, r2; int r, r2;
struct evutil_addrinfo *ai=NULL, *ai2=NULL; struct evutil_addrinfo *ai=NULL, *ai2=NULL;
struct evutil_addrinfo hints; struct evutil_addrinfo hints;
if (tested)
return need_hack;
memset(&hints,0,sizeof(hints)); memset(&hints,0,sizeof(hints));
hints.ai_family = PF_UNSPEC; hints.ai_family = PF_UNSPEC;
@ -999,21 +1003,40 @@ need_numeric_port_hack(void)
hints.ai_socktype = SOCK_STREAM; hints.ai_socktype = SOCK_STREAM;
r2 = getaddrinfo("1.2.3.4", "80", &hints, &ai2); r2 = getaddrinfo("1.2.3.4", "80", &hints, &ai2);
if (r2 == 0 && r != 0) { if (r2 == 0 && r != 0) {
need_hack=1; need_numeric_port_hack_=1;
} }
if (ai2 && ai2->ai_protocol == 0) {
need_socktype_protocol_hack_=1;
}
if (ai) if (ai)
freeaddrinfo(ai); freeaddrinfo(ai);
if (ai2) if (ai2)
freeaddrinfo(ai2); freeaddrinfo(ai2);
tested = 1; tested_for_getaddrinfo_hacks=1;
return need_hack; }
static inline int
need_numeric_port_hack(void)
{
if (!tested_for_getaddrinfo_hacks)
test_for_getaddrinfo_hacks();
return need_numeric_port_hack_;
}
static inline int
need_socktype_protocol_hack(void)
{
if (!tested_for_getaddrinfo_hacks)
test_for_getaddrinfo_hacks();
return need_socktype_protocol_hack_;
} }
static void static void
apply_numeric_port_hack(int port, struct evutil_addrinfo **ai) apply_numeric_port_hack(int port, struct evutil_addrinfo **ai)
{ {
/* Now we run through the list and set the ports on all of the /* Now we run through the list and set the ports on all of the
* results where ports */ * results where ports would make sense. */
for ( ; *ai; ai = &(*ai)->ai_next) { for ( ; *ai; ai = &(*ai)->ai_next) {
struct sockaddr *sa = (*ai)->ai_addr; struct sockaddr *sa = (*ai)->ai_addr;
if (sa && sa->sa_family == AF_INET) { if (sa && sa->sa_family == AF_INET) {
@ -1100,6 +1123,10 @@ evutil_getaddrinfo(const char *nodename, const char *servname,
servname = NULL; servname = NULL;
} }
if (need_socktype_protocol_hack()) {
evutil_getaddrinfo_infer_protocols(&hints);
}
/* Make sure that we didn't actually steal any AI_FLAGS values that /* Make sure that we didn't actually steal any AI_FLAGS values that
* the system is using. (This is a constant expression, and should ge * the system is using. (This is a constant expression, and should ge
* optimized out.) * optimized out.)
@ -1115,6 +1142,12 @@ evutil_getaddrinfo(const char *nodename, const char *servname,
err = getaddrinfo(nodename, servname, &hints, res); err = getaddrinfo(nodename, servname, &hints, res);
if (need_np_hack) if (need_np_hack)
apply_numeric_port_hack(portnum, res); apply_numeric_port_hack(portnum, res);
if (need_socktype_protocol_hack()) {
struct evutil_addrinfo *ai;
for (ai = *res; ai; ai = ai->ai_next)
evutil_getaddrinfo_infer_protocols(ai);
}
return err; return err;
#else #else
int port=0, err; int port=0, err;