@@ -355,3 +355,82 @@ impl FromInner<c_int> for Socket {
355
355
impl IntoInner < c_int > for Socket {
356
356
fn into_inner ( self ) -> c_int { self . 0 . into_raw ( ) }
357
357
}
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