]> git.lizzy.rs Git - rust.git/commitdiff
Only link res_init() on GNU/*nix
authorRyan Cumming <etaoins@gmail.com>
Wed, 10 Jan 2018 09:13:03 +0000 (20:13 +1100)
committerRyan Cumming <etaoins@gmail.com>
Mon, 15 Jan 2018 19:30:44 +0000 (06:30 +1100)
To workaround a bug in glibc <= 2.26 lookup_host() calls res_init()
based on the glibc version detected at runtime. While this avoids
calling res_init() on platforms where it's not required we will still
end up linking against the symbol.

This causes an issue on macOS where res_init() is implemented in a
separate library (libresolv.9.dylib) from the main libc. While this is
harmless for standalone programs it becomes a problem if Rust code is
statically linked against another program. If the linked program doesn't
already specify -lresolv it will cause the link to fail. This is
captured in issue #46797

Fix this by hooking in to the glibc workaround in `cvt_gai` and only
activating it for the "gnu" environment on Unix This should include all
glibc platforms while excluding musl, windows-gnu, macOS, FreeBSD, etc.

This has the side benefit of removing the #[cfg] in sys_common; only
unix.rs has code related to the workaround now.

src/libstd/sys/unix/l4re.rs
src/libstd/sys/unix/net.rs
src/libstd/sys_common/net.rs

index c3e8d0b7d95a8b31ca22b36dd8078bfd3fa078ec..21218489679393a92a655bf0012caa5ad242f962 100644 (file)
@@ -437,9 +437,5 @@ unsafe impl Send for LookupHost {}
     pub fn lookup_host(_: &str) -> io::Result<LookupHost> {
         unimpl!();
     }
-
-    pub fn res_init_if_glibc_before_2_26() -> io::Result<()> {
-        unimpl!();
-    }
 }
 
index e775f857f2b40a84b8df717f9ed6d30649a02b3c..3f65975e608800a7198d6c36bb84832e4a437a62 100644 (file)
@@ -51,6 +51,10 @@ pub fn cvt_gai(err: c_int) -> io::Result<()> {
     if err == 0 {
         return Ok(())
     }
+
+    // We may need to trigger a glibc workaround. See on_resolver_failure() for details.
+    on_resolver_failure();
+
     if err == EAI_SYSTEM {
         return Err(io::Error::last_os_error())
     }
@@ -377,21 +381,22 @@ fn into_inner(self) -> c_int { self.0.into_raw() }
 // res_init unconditionally, we call it only when we detect we're linking
 // against glibc version < 2.26. (That is, when we both know its needed and
 // believe it's thread-safe).
-pub fn res_init_if_glibc_before_2_26() -> io::Result<()> {
+#[cfg(target_env = "gnu")]
+fn on_resolver_failure() {
     // If the version fails to parse, we treat it the same as "not glibc".
     if let Some(Ok(version_str)) = glibc_version_cstr().map(CStr::to_str) {
         if let Some(version) = parse_glibc_version(version_str) {
             if version < (2, 26) {
-                let ret = unsafe { libc::res_init() };
-                if ret != 0 {
-                    return Err(io::Error::last_os_error());
-                }
+                unsafe { libc::res_init() };
             }
         }
     }
-    Ok(())
 }
 
+#[cfg(not(target_env = "gnu"))]
+fn on_resolver_failure() {}
+
+#[cfg(target_env = "gnu")]
 fn glibc_version_cstr() -> Option<&'static CStr> {
     weak! {
         fn gnu_get_libc_version() -> *const libc::c_char
@@ -405,6 +410,7 @@ fn gnu_get_libc_version() -> *const libc::c_char
 
 // Returns Some((major, minor)) if the string is a valid "x.y" version,
 // ignoring any extra dot-separated parts. Otherwise return None.
+#[cfg(target_env = "gnu")]
 fn parse_glibc_version(version: &str) -> Option<(usize, usize)> {
     let mut parsed_ints = version.split(".").map(str::parse::<usize>).fuse();
     match (parsed_ints.next(), parsed_ints.next()) {
@@ -413,7 +419,7 @@ fn parse_glibc_version(version: &str) -> Option<(usize, usize)> {
     }
 }
 
-#[cfg(test)]
+#[cfg(all(test, taget_env = "gnu"))]
 mod test {
     use super::*;
 
index c70b39995ebb0c45d3ae91a0c2959518baa79054..b841afe1a5141dfd2db749ad09077400c8cc1933 100644 (file)
@@ -166,27 +166,9 @@ pub fn lookup_host(host: &str) -> io::Result<LookupHost> {
     hints.ai_socktype = c::SOCK_STREAM;
     let mut res = ptr::null_mut();
     unsafe {
-        match cvt_gai(c::getaddrinfo(c_host.as_ptr(), ptr::null(), &hints, &mut res)) {
-            Ok(_) => {
-                Ok(LookupHost { original: res, cur: res })
-            },
-            #[cfg(unix)]
-            Err(e) => {
-                // If we're running glibc prior to version 2.26, the lookup
-                // failure could be caused by caching a stale /etc/resolv.conf.
-                // We need to call libc::res_init() to clear the cache. But we
-                // shouldn't call it in on any other platform, because other
-                // res_init implementations aren't thread-safe. See
-                // https://github.com/rust-lang/rust/issues/41570 and
-                // https://github.com/rust-lang/rust/issues/43592.
-                use sys::net::res_init_if_glibc_before_2_26;
-                let _ = res_init_if_glibc_before_2_26();
-                Err(e)
-            },
-            // the cfg is needed here to avoid an "unreachable pattern" warning
-            #[cfg(not(unix))]
-            Err(e) => Err(e),
-        }
+        cvt_gai(c::getaddrinfo(c_host.as_ptr(), ptr::null(), &hints, &mut res)).map(|_| {
+            LookupHost { original: res, cur: res }
+        })
     }
 }