@@ -355,3 +355,82 @@ impl FromInner<c_int> for Socket {
355355impl IntoInner < c_int > for Socket {
356356 fn into_inner ( self ) -> c_int { self . 0 . into_raw ( ) }
357357}
358+
359+ // In versions of glibc prior to 2.26, there's a bug where the DNS resolver
360+ // will cache the contents of /etc/resolv.conf, so changes to that file on disk
361+ // can be ignored by a long-running program. That can break DNS lookups on e.g.
362+ // laptops where the network comes and goes. See
363+ // https://sourceware.org/bugzilla/show_bug.cgi?id=984. Note however that some
364+ // distros including Debian have patched glibc to fix this for a long time.
365+ //
366+ // A workaround for this bug is to call the res_init libc function, to clear
367+ // the cached configs. Unfortunately, while we believe glibc's implementation
368+ // of res_init is thread-safe, we know that other implementations are not
369+ // (https://github.com/rust-lang/rust/issues/43592). Code here in libstd could
370+ // try to synchronize its res_init calls with a Mutex, but that wouldn't
371+ // protect programs that call into libc in other ways. So instead of calling
372+ // res_init unconditionally, we call it only when we detect we're linking
373+ // against glibc version < 2.26. (That is, when we both know its needed and
374+ // believe it's thread-safe).
375+ pub fn res_init_if_glibc_before_2_26 ( ) -> io:: Result < ( ) > {
376+ // If the version fails to parse, we treat it the same as "not glibc".
377+ if let Some ( Ok ( version_str) ) = glibc_version_cstr ( ) . map ( CStr :: to_str) {
378+ if let Some ( version) = parse_glibc_version ( version_str) {
379+ if version < ( 2 , 26 ) {
380+ let ret = unsafe { libc:: res_init ( ) } ;
381+ if ret != 0 {
382+ return Err ( io:: Error :: last_os_error ( ) ) ;
383+ }
384+ }
385+ }
386+ }
387+ Ok ( ( ) )
388+ }
389+
390+ fn glibc_version_cstr ( ) -> Option < & ' static CStr > {
391+ weak ! {
392+ fn gnu_get_libc_version( ) -> * const libc:: c_char
393+ }
394+ if let Some ( f) = gnu_get_libc_version. get ( ) {
395+ unsafe { Some ( CStr :: from_ptr ( f ( ) ) ) }
396+ } else {
397+ None
398+ }
399+ }
400+
401+ // Returns Some((major, minor)) if the string is a valid "x.y" version,
402+ // ignoring any extra dot-separated parts. Otherwise return None.
403+ fn parse_glibc_version ( version : & str ) -> Option < ( usize , usize ) > {
404+ let mut parsed_ints = version. split ( "." ) . map ( str:: parse :: < usize > ) . fuse ( ) ;
405+ match ( parsed_ints. next ( ) , parsed_ints. next ( ) ) {
406+ ( Some ( Ok ( major) ) , Some ( Ok ( minor) ) ) => Some ( ( major, minor) ) ,
407+ _ => None
408+ }
409+ }
410+
411+ #[ cfg( test) ]
412+ mod test {
413+ use super :: * ;
414+
415+ #[ test]
416+ fn test_res_init ( ) {
417+ // This mostly just tests that the weak linkage doesn't panic wildly...
418+ res_init_if_glibc_before_2_26 ( ) . unwrap ( ) ;
419+ }
420+
421+ #[ test]
422+ fn test_parse_glibc_version ( ) {
423+ let cases = [
424+ ( "0.0" , Some ( ( 0 , 0 ) ) ) ,
425+ ( "01.+2" , Some ( ( 1 , 2 ) ) ) ,
426+ ( "3.4.5.six" , Some ( ( 3 , 4 ) ) ) ,
427+ ( "1" , None ) ,
428+ ( "1.-2" , None ) ,
429+ ( "1.foo" , None ) ,
430+ ( "foo.1" , None ) ,
431+ ] ;
432+ for & ( version_str, parsed) in cases. iter ( ) {
433+ assert_eq ! ( parsed, parse_glibc_version( version_str) ) ;
434+ }
435+ }
436+ }
0 commit comments