@@ -15,10 +15,11 @@ use std::time::Duration;
15
15
use tokio:: net:: { TcpStream , UdpSocket } ;
16
16
use url:: { Host , Url } ;
17
17
18
- // Interweave v4 and v6 addresses as per RFC8305.
18
+ // Interleave v4 and v6 addresses as per RFC8305.
19
19
// The first address is v6 if we have any v6 addresses.
20
- pub fn sort_socket_addrs ( socket_addrs : & [ SocketAddr ] ) -> impl Iterator < Item = & ' _ SocketAddr > {
21
- let mut pick_v6 = false ;
20
+ #[ inline]
21
+ fn sort_socket_addrs ( socket_addrs : & [ SocketAddr ] , prefer_ipv6 : bool ) -> impl Iterator < Item = & ' _ SocketAddr > {
22
+ let mut pick_v6 = !prefer_ipv6;
22
23
let mut v6 = socket_addrs. iter ( ) . filter ( |s| matches ! ( s, SocketAddr :: V6 ( _) ) ) ;
23
24
let mut v4 = socket_addrs. iter ( ) . filter ( |s| matches ! ( s, SocketAddr :: V4 ( _) ) ) ;
24
25
std:: iter:: from_fn ( move || {
@@ -34,48 +35,63 @@ pub fn sort_socket_addrs(socket_addrs: &[SocketAddr]) -> impl Iterator<Item = &'
34
35
#[ derive( Clone ) ]
35
36
pub enum DnsResolver {
36
37
System ,
37
- TrustDns ( AsyncResolver < GenericConnector < TokioRuntimeProviderWithSoMark > > ) ,
38
+ TrustDns {
39
+ resolver : AsyncResolver < GenericConnector < TokioRuntimeProviderWithSoMark > > ,
40
+ prefer_ipv6 : bool ,
41
+ } ,
38
42
}
39
43
40
44
impl DnsResolver {
41
45
pub async fn lookup_host ( & self , domain : & str , port : u16 ) -> anyhow:: Result < Vec < SocketAddr > > {
42
46
let addrs: Vec < SocketAddr > = match self {
43
47
Self :: System => tokio:: net:: lookup_host ( format ! ( "{}:{}" , domain, port) ) . await ?. collect ( ) ,
44
- Self :: TrustDns ( dns_resolver) => dns_resolver
45
- . lookup_ip ( domain)
46
- . await ?
47
- . into_iter ( )
48
- . map ( |ip| match ip {
49
- IpAddr :: V4 ( ip) => SocketAddr :: V4 ( SocketAddrV4 :: new ( ip, port) ) ,
50
- IpAddr :: V6 ( ip) => SocketAddr :: V6 ( SocketAddrV6 :: new ( ip, port, 0 , 0 ) ) ,
51
- } )
52
- . collect ( ) ,
48
+ Self :: TrustDns { resolver, prefer_ipv6 } => {
49
+ let addrs: Vec < _ > = resolver
50
+ . lookup_ip ( domain)
51
+ . await ?
52
+ . into_iter ( )
53
+ . map ( |ip| match ip {
54
+ IpAddr :: V4 ( ip) => SocketAddr :: V4 ( SocketAddrV4 :: new ( ip, port) ) ,
55
+ IpAddr :: V6 ( ip) => SocketAddr :: V6 ( SocketAddrV6 :: new ( ip, port, 0 , 0 ) ) ,
56
+ } )
57
+ . collect ( ) ;
58
+ sort_socket_addrs ( & addrs, * prefer_ipv6) . copied ( ) . collect ( )
59
+ }
53
60
} ;
54
61
55
62
Ok ( addrs)
56
63
}
57
64
58
- pub fn new_from_urls ( resolvers : & [ Url ] , proxy : Option < Url > , so_mark : Option < u32 > ) -> anyhow:: Result < Self > {
65
+ pub fn new_from_urls (
66
+ resolvers : & [ Url ] ,
67
+ proxy : Option < Url > ,
68
+ so_mark : Option < u32 > ,
69
+ prefer_ipv6 : bool ,
70
+ ) -> anyhow:: Result < Self > {
59
71
if resolvers. is_empty ( ) {
60
72
// no dns resolver specified, fall-back to default one
61
73
let Ok ( ( cfg, mut opts) ) = hickory_resolver:: system_conf:: read_system_conf ( ) else {
62
74
warn ! ( "Fall-backing to system dns resolver. You should consider specifying a dns resolver. To avoid performance issue" ) ;
63
75
return Ok ( Self :: System ) ;
64
76
} ;
65
77
66
- opts. timeout = std:: time:: Duration :: from_secs ( 1 ) ;
78
+ opts. ip_strategy = LookupIpStrategy :: Ipv4AndIpv6 ;
79
+ opts. timeout = Duration :: from_secs ( 1 ) ;
67
80
// Windows end-up with too many dns resolvers, which causes a performance issue
68
81
// https://github.com/hickory-dns/hickory-dns/issues/1968
69
82
#[ cfg( target_os = "windows" ) ]
70
83
{
71
84
opts. cache_size = 1024 ;
72
85
opts. num_concurrent_reqs = cfg. name_servers ( ) . len ( ) ;
73
86
}
74
- return Ok ( Self :: TrustDns ( AsyncResolver :: new (
75
- cfg,
76
- opts,
77
- GenericConnector :: new ( TokioRuntimeProviderWithSoMark :: new ( proxy, so_mark) ) ,
78
- ) ) ) ;
87
+ return Ok ( Self :: TrustDns {
88
+ resolver : AsyncResolver :: new (
89
+ cfg,
90
+ opts,
91
+ GenericConnector :: new ( TokioRuntimeProviderWithSoMark :: new ( proxy, so_mark) ) ,
92
+ ) ,
93
+ prefer_ipv6,
94
+ } ) ;
79
95
} ;
80
96
81
97
// if one is specified as system, use the default one from libc
@@ -127,13 +143,16 @@ impl DnsResolver {
127
143
}
128
144
129
145
let mut opts = ResolverOpts :: default ( ) ;
130
- opts. timeout = std :: time :: Duration :: from_secs ( 1 ) ;
146
+ opts. timeout = Duration :: from_secs ( 1 ) ;
131
147
opts. ip_strategy = LookupIpStrategy :: Ipv4AndIpv6 ;
132
- Ok ( Self :: TrustDns ( AsyncResolver :: new (
133
- cfg,
134
- opts,
135
- GenericConnector :: new ( TokioRuntimeProviderWithSoMark :: new ( proxy, so_mark) ) ,
136
- ) ) )
148
+ Ok ( Self :: TrustDns {
149
+ resolver : AsyncResolver :: new (
150
+ cfg,
151
+ opts,
152
+ GenericConnector :: new ( TokioRuntimeProviderWithSoMark :: new ( proxy, so_mark) ) ,
153
+ ) ,
154
+ prefer_ipv6,
155
+ } )
137
156
}
138
157
}
139
158
@@ -235,3 +254,29 @@ impl RuntimeProvider for TokioRuntimeProviderWithSoMark {
235
254
Box :: pin ( socket)
236
255
}
237
256
}
257
+
258
+ #[ cfg( test) ]
259
+ mod tests {
260
+ use crate :: dns:: sort_socket_addrs;
261
+ use std:: net:: { Ipv4Addr , Ipv6Addr , SocketAddr , SocketAddrV4 , SocketAddrV6 } ;
262
+
263
+ #[ test]
264
+ fn test_sort_socket_addrs ( ) {
265
+ let addrs = [
266
+ SocketAddr :: V4 ( SocketAddrV4 :: new ( Ipv4Addr :: new ( 127 , 0 , 0 , 1 ) , 1 ) ) ,
267
+ SocketAddr :: V4 ( SocketAddrV4 :: new ( Ipv4Addr :: new ( 127 , 0 , 0 , 2 ) , 1 ) ) ,
268
+ SocketAddr :: V6 ( SocketAddrV6 :: new ( Ipv6Addr :: new ( 0 , 0 , 0 , 0 , 127 , 0 , 0 , 1 ) , 1 , 0 , 0 ) ) ,
269
+ SocketAddr :: V4 ( SocketAddrV4 :: new ( Ipv4Addr :: new ( 127 , 0 , 0 , 3 ) , 1 ) ) ,
270
+ SocketAddr :: V6 ( SocketAddrV6 :: new ( Ipv6Addr :: new ( 0 , 0 , 0 , 0 , 127 , 0 , 0 , 2 ) , 1 , 0 , 0 ) ) ,
271
+ ] ;
272
+ let expected = [
273
+ SocketAddr :: V6 ( SocketAddrV6 :: new ( Ipv6Addr :: new ( 0 , 0 , 0 , 0 , 127 , 0 , 0 , 1 ) , 1 , 0 , 0 ) ) ,
274
+ SocketAddr :: V4 ( SocketAddrV4 :: new ( Ipv4Addr :: new ( 127 , 0 , 0 , 1 ) , 1 ) ) ,
275
+ SocketAddr :: V6 ( SocketAddrV6 :: new ( Ipv6Addr :: new ( 0 , 0 , 0 , 0 , 127 , 0 , 0 , 2 ) , 1 , 0 , 0 ) ) ,
276
+ SocketAddr :: V4 ( SocketAddrV4 :: new ( Ipv4Addr :: new ( 127 , 0 , 0 , 2 ) , 1 ) ) ,
277
+ SocketAddr :: V4 ( SocketAddrV4 :: new ( Ipv4Addr :: new ( 127 , 0 , 0 , 3 ) , 1 ) ) ,
278
+ ] ;
279
+ let actual: Vec < _ > = sort_socket_addrs ( & addrs, true ) . copied ( ) . collect ( ) ;
280
+ assert_eq ! ( expected, * actual) ;
281
+ }
282
+ }
0 commit comments