deltachat/net/
dns.rs

1//! DNS resolution and cache.
2//!
3//! DNS cache in Delta Chat has two layers:
4//! in-memory cache and persistent `dns_cache` SQL table.
5//!
6//! In-memory cache is using a "stale-while-revalidate" strategy.
7//! If there is a cached value, it is returned immediately
8//! and revalidation task is started in the background
9//! to replace old cached IP addresses with new ones.
10//! If there is no cached value yet,
11//! lookup only finishes when `lookup_host` returns first results.
12//! In-memory cache is shared between all accounts
13//! and is never stored on the disk.
14//! It can be thought of as an extension
15//! of the system resolver.
16//!
17//! Persistent `dns_cache` SQL table is used to collect
18//! all IP addresses ever seen for the hostname
19//! together with the timestamp
20//! of the last time IP address has been seen.
21//! Note that this timestamp reflects the time
22//! IP address was returned by the in-memory cache
23//! rather than the underlying system resolver.
24//! Unused entries are removed after 30 days
25//! (`CACHE_TTL` constant) to avoid having
26//! old non-working IP addresses in the cache indefinitely.
27//!
28//! When Delta Chat needs an IP address for the host,
29//! it queries in-memory cache for the next result
30//! and merges the list of IP addresses
31//! with the list of IP addresses from persistent cache.
32//! Resulting list is constructed
33//! by taking the first two results from the resolver
34//! followed up by persistent cache results
35//! and terminated by the rest of resolver results.
36//!
37//! Persistent cache results are sorted
38//! by the time of the most recent successful connection
39//! using the result. For results that have never been
40//! used for successful connection timestamp of
41//! retrieving them from in-memory cache is used.
42
43use anyhow::{Context as _, Result};
44use std::collections::HashMap;
45use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
46use std::str::FromStr;
47use std::sync::LazyLock;
48use tokio::net::lookup_host;
49use tokio::time::timeout;
50
51use super::load_connection_timestamp;
52use crate::context::Context;
53use crate::tools::time;
54
55/// Inserts entry into DNS cache
56/// or updates existing one with a new timestamp.
57async fn update_cache(context: &Context, host: &str, addr: &str, now: i64) -> Result<()> {
58    context
59        .sql
60        .execute(
61            "INSERT INTO dns_cache
62             (hostname, address, timestamp)
63             VALUES (?, ?, ?)
64             ON CONFLICT (hostname, address)
65             DO UPDATE SET timestamp=excluded.timestamp",
66            (host, addr, now),
67        )
68        .await?;
69    Ok(())
70}
71
72pub(crate) async fn prune_dns_cache(context: &Context) -> Result<()> {
73    let now = time();
74    context
75        .sql
76        .execute(
77            "DELETE FROM dns_cache
78             WHERE ? > timestamp + ?",
79            (now, super::CACHE_TTL),
80        )
81        .await?;
82    Ok(())
83}
84
85/// Map from hostname to IP addresses.
86///
87/// NOTE: sync RwLock is used, so it must not be held across `.await`
88/// to avoid deadlocks.
89/// See
90/// <https://docs.rs/tokio/1.40.0/tokio/sync/struct.Mutex.html#which-kind-of-mutex-should-you-use>
91/// and
92/// <https://stackoverflow.com/questions/63712823/why-do-i-get-a-deadlock-when-using-tokio-with-a-stdsyncmutex>.
93static LOOKUP_HOST_CACHE: LazyLock<parking_lot::RwLock<HashMap<String, Vec<IpAddr>>>> =
94    LazyLock::new(Default::default);
95
96/// Wrapper for `lookup_host` that returns IP addresses.
97async fn lookup_ips(host: impl tokio::net::ToSocketAddrs) -> Result<impl Iterator<Item = IpAddr>> {
98    Ok(lookup_host(host)
99        .await
100        .context("DNS lookup failure")?
101        .map(|addr| addr.ip()))
102}
103
104async fn lookup_host_with_memory_cache(
105    context: &Context,
106    hostname: &str,
107    port: u16,
108) -> Result<Vec<IpAddr>> {
109    let stale_result = {
110        let rwlock_read_guard = LOOKUP_HOST_CACHE.read();
111        rwlock_read_guard.get(hostname).cloned()
112    };
113    if let Some(stale_result) = stale_result {
114        // Revalidate the cache in the background.
115        {
116            let context = context.clone();
117            let hostname = hostname.to_string();
118            tokio::spawn(async move {
119                match lookup_ips((hostname.clone(), port)).await {
120                    Ok(res) => {
121                        LOOKUP_HOST_CACHE.write().insert(hostname, res.collect());
122                    }
123                    Err(err) => {
124                        warn!(
125                            context,
126                            "Failed to revalidate results for {hostname:?}: {err:#}."
127                        );
128                    }
129                }
130            });
131        }
132
133        info!(
134            context,
135            "Using memory-cached DNS resolution for {hostname}."
136        );
137        Ok(stale_result)
138    } else {
139        info!(
140            context,
141            "No memory-cached DNS resolution for {hostname} available, waiting for the resolver."
142        );
143        let res: Vec<IpAddr> = lookup_ips((hostname, port)).await?.collect();
144
145        // Insert initial result into the cache.
146        //
147        // There may already be a result from a parallel
148        // task stored, overwriting it is not a problem.
149        LOOKUP_HOST_CACHE
150            .write()
151            .insert(hostname.to_string(), res.clone());
152        Ok(res)
153    }
154}
155
156/// Looks up the hostname and updates
157/// persistent DNS cache on success.
158async fn lookup_host_and_update_cache(
159    context: &Context,
160    hostname: &str,
161    port: u16,
162    now: i64,
163) -> Result<Vec<SocketAddr>> {
164    let res: Vec<IpAddr> = timeout(
165        super::TIMEOUT,
166        lookup_host_with_memory_cache(context, hostname, port),
167    )
168    .await
169    .context("DNS lookup timeout")?
170    .context("DNS lookup with memory cache failure")?;
171
172    for ip in &res {
173        let ip_string = ip.to_string();
174        if ip_string == hostname {
175            // IP address resolved into itself, not interesting to cache.
176            continue;
177        }
178
179        info!(context, "Resolved {hostname} into {ip}.");
180
181        // Update the cache.
182        update_cache(context, hostname, &ip_string, now).await?;
183    }
184
185    let res = res
186        .into_iter()
187        .map(|ip| SocketAddr::new(ip, port))
188        .collect();
189    Ok(res)
190}
191
192// Updates timestamp of the cached entry
193// or inserts a new one if cached entry does not exist.
194//
195// This function should be called when a successful TLS
196// connection is established with strict TLS checks.
197//
198// This increases priority of existing cached entries
199// and copies fallback addresses from built-in cache
200// into database cache on successful use.
201//
202// Unlike built-in cache,
203// database cache is used even if DNS
204// resolver returns a non-empty
205// (but potentially incorrect and unusable) result.
206pub(crate) async fn update_connect_timestamp(
207    context: &Context,
208    host: &str,
209    address: &str,
210) -> Result<()> {
211    if host == address {
212        return Ok(());
213    }
214
215    context
216        .sql
217        .execute(
218            "INSERT INTO dns_cache (hostname, address, timestamp)
219                 VALUES (?, ?, ?)
220                 ON CONFLICT (hostname, address)
221                 DO UPDATE SET timestamp=excluded.timestamp",
222            (host, address, time()),
223        )
224        .await?;
225    Ok(())
226}
227
228/// Preloaded DNS results that can be used in case of DNS server failures.
229///
230/// See <https://support.delta.chat/t/no-dns-resolution-result/2778> and
231/// <https://github.com/deltachat/deltachat-core-rust/issues/4920> for reasons.
232static DNS_PRELOAD: LazyLock<HashMap<&'static str, Vec<IpAddr>>> = LazyLock::new(|| {
233    HashMap::from([
234        (
235            "mail.sangham.net",
236            vec![
237                IpAddr::V4(Ipv4Addr::new(159, 69, 186, 85)),
238                IpAddr::V6(Ipv6Addr::new(0x2a01, 0x4f8, 0xc17, 0x798c, 0, 0, 0, 1)),
239            ],
240        ),
241        (
242            "nine.testrun.org",
243            vec![
244                IpAddr::V4(Ipv4Addr::new(116, 202, 233, 236)),
245                IpAddr::V4(Ipv4Addr::new(128, 140, 126, 197)),
246                IpAddr::V4(Ipv4Addr::new(49, 12, 116, 128)),
247                IpAddr::V6(Ipv6Addr::new(0x2a01, 0x4f8, 0x241, 0x4ce8, 0, 0, 0, 2)),
248            ],
249        ),
250        (
251            "disroot.org",
252            vec![IpAddr::V4(Ipv4Addr::new(178, 21, 23, 139))],
253        ),
254        (
255            "imap.gmail.com",
256            vec![
257                IpAddr::V4(Ipv4Addr::new(142, 250, 110, 108)),
258                IpAddr::V4(Ipv4Addr::new(142, 250, 110, 109)),
259                IpAddr::V4(Ipv4Addr::new(66, 102, 1, 108)),
260                IpAddr::V4(Ipv4Addr::new(66, 102, 1, 109)),
261                IpAddr::V6(Ipv6Addr::new(0x2a00, 0x1450, 0x400c, 0xc1f, 0, 0, 0, 0x6c)),
262                IpAddr::V6(Ipv6Addr::new(0x2a00, 0x1450, 0x400c, 0xc1f, 0, 0, 0, 0x6d)),
263            ],
264        ),
265        (
266            "smtp.gmail.com",
267            vec![
268                IpAddr::V4(Ipv4Addr::new(142, 250, 110, 109)),
269                IpAddr::V6(Ipv6Addr::new(0x2a00, 0x1450, 0x4013, 0xc04, 0, 0, 0, 0x6c)),
270            ],
271        ),
272        (
273            "mail.autistici.org",
274            vec![
275                IpAddr::V4(Ipv4Addr::new(198, 167, 222, 108)),
276                IpAddr::V4(Ipv4Addr::new(82, 94, 249, 234)),
277                IpAddr::V4(Ipv4Addr::new(93, 190, 126, 19)),
278            ],
279        ),
280        (
281            "smtp.autistici.org",
282            vec![
283                IpAddr::V4(Ipv4Addr::new(198, 167, 222, 108)),
284                IpAddr::V4(Ipv4Addr::new(82, 94, 249, 234)),
285                IpAddr::V4(Ipv4Addr::new(93, 190, 126, 19)),
286            ],
287        ),
288        (
289            "daleth.cafe",
290            vec![IpAddr::V4(Ipv4Addr::new(37, 27, 6, 204))],
291        ),
292        (
293            "imap.163.com",
294            vec![IpAddr::V4(Ipv4Addr::new(111, 124, 203, 45))],
295        ),
296        (
297            "smtp.163.com",
298            vec![IpAddr::V4(Ipv4Addr::new(103, 129, 252, 45))],
299        ),
300        (
301            "imap.aol.com",
302            vec![
303                IpAddr::V4(Ipv4Addr::new(212, 82, 101, 33)),
304                IpAddr::V4(Ipv4Addr::new(87, 248, 98, 69)),
305            ],
306        ),
307        (
308            "smtp.aol.com",
309            vec![IpAddr::V4(Ipv4Addr::new(87, 248, 97, 31))],
310        ),
311        (
312            "mail.arcor.de",
313            vec![IpAddr::V4(Ipv4Addr::new(2, 207, 150, 234))],
314        ),
315        (
316            "imap.arcor.de",
317            vec![IpAddr::V4(Ipv4Addr::new(2, 207, 150, 230))],
318        ),
319        (
320            "imap.fastmail.com",
321            vec![
322                IpAddr::V4(Ipv4Addr::new(103, 168, 172, 43)),
323                IpAddr::V4(Ipv4Addr::new(103, 168, 172, 58)),
324            ],
325        ),
326        (
327            "smtp.fastmail.com",
328            vec![
329                IpAddr::V4(Ipv4Addr::new(103, 168, 172, 45)),
330                IpAddr::V4(Ipv4Addr::new(103, 168, 172, 60)),
331            ],
332        ),
333        (
334            "imap.gmx.net",
335            vec![
336                IpAddr::V4(Ipv4Addr::new(212, 227, 17, 170)),
337                IpAddr::V4(Ipv4Addr::new(212, 227, 17, 186)),
338            ],
339        ),
340        (
341            "imap.mail.de",
342            vec![IpAddr::V4(Ipv4Addr::new(62, 201, 172, 16))],
343        ),
344        (
345            "smtp.mailbox.org",
346            vec![IpAddr::V4(Ipv4Addr::new(185, 97, 174, 196))],
347        ),
348        (
349            "imap.mailbox.org",
350            vec![IpAddr::V4(Ipv4Addr::new(185, 97, 174, 199))],
351        ),
352        (
353            "imap.naver.com",
354            vec![IpAddr::V4(Ipv4Addr::new(125, 209, 238, 153))],
355        ),
356        (
357            "imap.ouvaton.coop",
358            vec![IpAddr::V4(Ipv4Addr::new(194, 36, 166, 20))],
359        ),
360        (
361            "imap.purelymail.com",
362            vec![IpAddr::V4(Ipv4Addr::new(18, 204, 123, 63))],
363        ),
364        (
365            "imap.tiscali.it",
366            vec![IpAddr::V4(Ipv4Addr::new(213, 205, 33, 10))],
367        ),
368        (
369            "smtp.tiscali.it",
370            vec![IpAddr::V4(Ipv4Addr::new(213, 205, 33, 13))],
371        ),
372        (
373            "imap.web.de",
374            vec![
375                IpAddr::V4(Ipv4Addr::new(212, 227, 17, 162)),
376                IpAddr::V4(Ipv4Addr::new(212, 227, 17, 178)),
377            ],
378        ),
379        (
380            "imap.ziggo.nl",
381            vec![IpAddr::V4(Ipv4Addr::new(84, 116, 6, 3))],
382        ),
383        (
384            "imap.zoho.eu",
385            vec![IpAddr::V4(Ipv4Addr::new(185, 230, 214, 25))],
386        ),
387        (
388            "imaps.bluewin.ch",
389            vec![
390                IpAddr::V4(Ipv4Addr::new(16, 62, 253, 42)),
391                IpAddr::V4(Ipv4Addr::new(16, 63, 141, 244)),
392                IpAddr::V4(Ipv4Addr::new(16, 63, 146, 183)),
393            ],
394        ),
395        (
396            "mail.buzon.uy",
397            vec![IpAddr::V4(Ipv4Addr::new(185, 101, 93, 79))],
398        ),
399        (
400            "mail.ecloud.global",
401            vec![IpAddr::V4(Ipv4Addr::new(95, 217, 246, 96))],
402        ),
403        (
404            "mail.ende.in.net",
405            vec![IpAddr::V4(Ipv4Addr::new(95, 217, 5, 72))],
406        ),
407        (
408            "mail.gmx.net",
409            vec![
410                IpAddr::V4(Ipv4Addr::new(212, 227, 17, 168)),
411                IpAddr::V4(Ipv4Addr::new(212, 227, 17, 190)),
412            ],
413        ),
414        (
415            "mail.infomaniak.com",
416            vec![
417                IpAddr::V4(Ipv4Addr::new(83, 166, 143, 44)),
418                IpAddr::V4(Ipv4Addr::new(83, 166, 143, 45)),
419            ],
420        ),
421        (
422            "mail.mymagenta.at",
423            vec![IpAddr::V4(Ipv4Addr::new(80, 109, 253, 241))],
424        ),
425        (
426            "mail.nubo.coop",
427            vec![IpAddr::V4(Ipv4Addr::new(79, 99, 201, 10))],
428        ),
429        (
430            "mail.riseup.net",
431            vec![
432                IpAddr::V4(Ipv4Addr::new(198, 252, 153, 70)),
433                IpAddr::V4(Ipv4Addr::new(198, 252, 153, 71)),
434            ],
435        ),
436        (
437            "mail.systemausfall.org",
438            vec![
439                IpAddr::V4(Ipv4Addr::new(51, 75, 71, 249)),
440                IpAddr::V4(Ipv4Addr::new(80, 153, 252, 42)),
441            ],
442        ),
443        (
444            "mail.systemli.org",
445            vec![IpAddr::V4(Ipv4Addr::new(93, 190, 126, 36))],
446        ),
447        (
448            "mehl.cloud",
449            vec![IpAddr::V4(Ipv4Addr::new(95, 217, 223, 172))],
450        ),
451        (
452            "mx.freenet.de",
453            vec![
454                IpAddr::V4(Ipv4Addr::new(195, 4, 92, 210)),
455                IpAddr::V4(Ipv4Addr::new(195, 4, 92, 211)),
456                IpAddr::V4(Ipv4Addr::new(195, 4, 92, 212)),
457                IpAddr::V4(Ipv4Addr::new(195, 4, 92, 213)),
458            ],
459        ),
460        (
461            "newyear.aktivix.org",
462            vec![IpAddr::V4(Ipv4Addr::new(162, 247, 75, 192))],
463        ),
464        (
465            "pimap.schulon.org",
466            vec![IpAddr::V4(Ipv4Addr::new(194, 77, 246, 20))],
467        ),
468        (
469            "posteo.de",
470            vec![
471                IpAddr::V4(Ipv4Addr::new(185, 67, 36, 168)),
472                IpAddr::V4(Ipv4Addr::new(185, 67, 36, 169)),
473            ],
474        ),
475        (
476            "psmtp.schulon.org",
477            vec![IpAddr::V4(Ipv4Addr::new(194, 77, 246, 20))],
478        ),
479        (
480            "secureimap.t-online.de",
481            vec![
482                IpAddr::V4(Ipv4Addr::new(194, 25, 134, 114)),
483                IpAddr::V4(Ipv4Addr::new(194, 25, 134, 115)),
484                IpAddr::V4(Ipv4Addr::new(194, 25, 134, 50)),
485                IpAddr::V4(Ipv4Addr::new(194, 25, 134, 51)),
486            ],
487        ),
488        (
489            "securesmtp.t-online.de",
490            vec![
491                IpAddr::V4(Ipv4Addr::new(194, 25, 134, 110)),
492                IpAddr::V4(Ipv4Addr::new(194, 25, 134, 46)),
493            ],
494        ),
495        (
496            "smtp.aliyun.com",
497            vec![IpAddr::V4(Ipv4Addr::new(47, 246, 136, 232))],
498        ),
499        (
500            "smtp.mail.de",
501            vec![IpAddr::V4(Ipv4Addr::new(62, 201, 172, 21))],
502        ),
503        (
504            "smtp.mail.ru",
505            vec![
506                IpAddr::V4(Ipv4Addr::new(217, 69, 139, 160)),
507                IpAddr::V4(Ipv4Addr::new(94, 100, 180, 160)),
508            ],
509        ),
510        (
511            "imap.mail.yahoo.com",
512            vec![
513                IpAddr::V4(Ipv4Addr::new(87, 248, 103, 8)),
514                IpAddr::V4(Ipv4Addr::new(212, 82, 101, 24)),
515            ],
516        ),
517        (
518            "smtp.mail.yahoo.com",
519            vec![IpAddr::V4(Ipv4Addr::new(87, 248, 97, 36))],
520        ),
521        (
522            "imap.mailo.com",
523            vec![IpAddr::V4(Ipv4Addr::new(213, 182, 54, 20))],
524        ),
525        (
526            "smtp.mailo.com",
527            vec![IpAddr::V4(Ipv4Addr::new(213, 182, 54, 20))],
528        ),
529        (
530            "smtp.naver.com",
531            vec![IpAddr::V4(Ipv4Addr::new(125, 209, 238, 155))],
532        ),
533        (
534            "smtp.ouvaton.coop",
535            vec![IpAddr::V4(Ipv4Addr::new(194, 36, 166, 20))],
536        ),
537        (
538            "smtp.purelymail.com",
539            vec![IpAddr::V4(Ipv4Addr::new(18, 204, 123, 63))],
540        ),
541        (
542            "imap.qq.com",
543            vec![IpAddr::V4(Ipv4Addr::new(43, 129, 255, 54))],
544        ),
545        (
546            "smtp.qq.com",
547            vec![IpAddr::V4(Ipv4Addr::new(43, 129, 255, 54))],
548        ),
549        (
550            "imap.rambler.ru",
551            vec![
552                IpAddr::V4(Ipv4Addr::new(81, 19, 77, 169)),
553                IpAddr::V4(Ipv4Addr::new(81, 19, 77, 171)),
554                IpAddr::V4(Ipv4Addr::new(81, 19, 77, 168)),
555                IpAddr::V4(Ipv4Addr::new(81, 19, 77, 170)),
556            ],
557        ),
558        (
559            "smtp.rambler.ru",
560            vec![
561                IpAddr::V4(Ipv4Addr::new(81, 19, 77, 165)),
562                IpAddr::V4(Ipv4Addr::new(81, 19, 77, 167)),
563                IpAddr::V4(Ipv4Addr::new(81, 19, 77, 166)),
564                IpAddr::V4(Ipv4Addr::new(81, 19, 77, 164)),
565            ],
566        ),
567        (
568            "imap.vivaldi.net",
569            vec![IpAddr::V4(Ipv4Addr::new(31, 209, 137, 15))],
570        ),
571        (
572            "smtp.vivaldi.net",
573            vec![IpAddr::V4(Ipv4Addr::new(31, 209, 137, 12))],
574        ),
575        (
576            "imap.vodafonemail.de",
577            vec![IpAddr::V4(Ipv4Addr::new(2, 207, 150, 230))],
578        ),
579        (
580            "smtp.vodafonemail.de",
581            vec![IpAddr::V4(Ipv4Addr::new(2, 207, 150, 234))],
582        ),
583        (
584            "smtp.web.de",
585            vec![
586                IpAddr::V4(Ipv4Addr::new(213, 165, 67, 108)),
587                IpAddr::V4(Ipv4Addr::new(213, 165, 67, 124)),
588            ],
589        ),
590        (
591            "imap.yandex.com",
592            vec![IpAddr::V4(Ipv4Addr::new(77, 88, 21, 125))],
593        ),
594        (
595            "smtp.yandex.com",
596            vec![IpAddr::V4(Ipv4Addr::new(77, 88, 21, 158))],
597        ),
598        (
599            "smtp.ziggo.nl",
600            vec![IpAddr::V4(Ipv4Addr::new(84, 116, 6, 3))],
601        ),
602        (
603            "smtp.zoho.eu",
604            vec![IpAddr::V4(Ipv4Addr::new(185, 230, 212, 164))],
605        ),
606        (
607            "smtpauths.bluewin.ch",
608            vec![IpAddr::V4(Ipv4Addr::new(195, 186, 120, 54))],
609        ),
610        (
611            "stinpriza.net",
612            vec![IpAddr::V4(Ipv4Addr::new(5, 9, 122, 184))],
613        ),
614        (
615            "undernet.uy",
616            vec![IpAddr::V4(Ipv4Addr::new(167, 62, 254, 153))],
617        ),
618        (
619            "webbox222.server-home.org",
620            vec![IpAddr::V4(Ipv4Addr::new(91, 203, 111, 88))],
621        ),
622    ])
623});
624
625async fn lookup_cache(
626    context: &Context,
627    host: &str,
628    port: u16,
629    alpn: &str,
630    now: i64,
631) -> Result<Vec<SocketAddr>> {
632    let mut res = Vec::new();
633    for cached_address in context
634        .sql
635        .query_map(
636            "SELECT dns_cache.address
637             FROM dns_cache
638             LEFT JOIN connection_history
639               ON dns_cache.hostname = connection_history.host
640               AND dns_cache.address = connection_history.addr
641               AND connection_history.port = ?
642               AND connection_history.alpn = ?
643             WHERE dns_cache.hostname = ?
644             AND ? < dns_cache.timestamp + ?
645             ORDER BY IFNULL(connection_history.timestamp, dns_cache.timestamp) DESC
646             LIMIT 50",
647            (port, alpn, host, now, super::CACHE_TTL),
648            |row| {
649                let address: String = row.get(0)?;
650                Ok(address)
651            },
652            |rows| {
653                rows.collect::<std::result::Result<Vec<String>, _>>()
654                    .map_err(Into::into)
655            },
656        )
657        .await?
658    {
659        match IpAddr::from_str(&cached_address) {
660            Ok(ip_addr) => {
661                let addr = SocketAddr::new(ip_addr, port);
662                res.push(addr);
663            }
664            Err(err) => {
665                warn!(
666                    context,
667                    "Failed to parse cached address {:?}: {:#}.", cached_address, err
668                );
669            }
670        }
671    }
672    Ok(res)
673}
674
675/// Sorts DNS resolution results by connection timestamp in descending order
676/// so IP addresses that we recently connected to successfully are tried first.
677async fn sort_by_connection_timestamp(
678    context: &Context,
679    input: Vec<SocketAddr>,
680    alpn: &str,
681    host: &str,
682) -> Result<Vec<SocketAddr>> {
683    let mut res: Vec<(Option<i64>, SocketAddr)> = Vec::with_capacity(input.len());
684    for addr in input {
685        let timestamp = load_connection_timestamp(
686            &context.sql,
687            alpn,
688            host,
689            addr.port(),
690            Some(&addr.ip().to_string()),
691        )
692        .await?;
693        res.push((timestamp, addr));
694    }
695    res.sort_by_key(|(ts, _addr)| std::cmp::Reverse(*ts));
696    Ok(res.into_iter().map(|(_ts, addr)| addr).collect())
697}
698
699/// Looks up hostname and port using DNS and updates the address resolution cache.
700///
701/// `alpn` is used to sort DNS results by the time we have successfully
702/// connected to the IP address using given `alpn`.
703/// If result sorting is not needed or `alpn` is unknown,
704/// pass empty string here, e.g. for HTTP requests
705/// or when resolving the IP address of SOCKS proxy.
706///
707/// If `load_cache` is true, appends cached results not older than 30 days to the end
708/// or entries from fallback cache if there are no cached addresses.
709pub(crate) async fn lookup_host_with_cache(
710    context: &Context,
711    hostname: &str,
712    port: u16,
713    alpn: &str,
714    load_cache: bool,
715) -> Result<Vec<SocketAddr>> {
716    let now = time();
717    let resolved_addrs = match lookup_host_and_update_cache(context, hostname, port, now).await {
718        Ok(res) => {
719            if alpn.is_empty() {
720                res
721            } else {
722                sort_by_connection_timestamp(context, res, alpn, hostname).await?
723            }
724        }
725        Err(err) => {
726            warn!(
727                context,
728                "DNS resolution for {hostname}:{port} failed: {err:#}."
729            );
730            Vec::new()
731        }
732    };
733
734    if load_cache {
735        let mut cache = lookup_cache(context, hostname, port, alpn, now).await?;
736        if let Some(ips) = DNS_PRELOAD.get(hostname) {
737            for ip in ips {
738                let addr = SocketAddr::new(*ip, port);
739                if !cache.contains(&addr) {
740                    cache.push(addr);
741                }
742            }
743        }
744
745        Ok(merge_with_cache(resolved_addrs, cache))
746    } else {
747        Ok(resolved_addrs)
748    }
749}
750
751/// Merges results received from DNS with cached results.
752///
753/// At most 10 results are returned.
754fn merge_with_cache(
755    mut resolved_addrs: Vec<SocketAddr>,
756    cache: Vec<SocketAddr>,
757) -> Vec<SocketAddr> {
758    let rest = resolved_addrs.split_off(std::cmp::min(resolved_addrs.len(), 2));
759
760    for addr in cache.into_iter().chain(rest.into_iter()) {
761        if !resolved_addrs.contains(&addr) {
762            resolved_addrs.push(addr);
763            if resolved_addrs.len() >= 10 {
764                break;
765            }
766        }
767    }
768
769    resolved_addrs
770}
771
772#[cfg(test)]
773mod tests {
774    use super::*;
775
776    use crate::net::update_connection_history;
777    use crate::test_utils::TestContext;
778
779    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
780    async fn test_sort_by_connection_timestamp() {
781        let alice = &TestContext::new_alice().await;
782        let now = time();
783
784        let ipv6_addr = IpAddr::V6(Ipv6Addr::new(0x2a01, 0x4f8, 0x241, 0x4ce8, 0, 0, 0, 2));
785        let ipv4_addr = IpAddr::V4(Ipv4Addr::new(116, 202, 233, 236));
786
787        assert_eq!(
788            sort_by_connection_timestamp(
789                alice,
790                vec![
791                    SocketAddr::new(ipv6_addr, 993),
792                    SocketAddr::new(ipv4_addr, 993)
793                ],
794                "imap",
795                "nine.testrun.org"
796            )
797            .await
798            .unwrap(),
799            vec![
800                SocketAddr::new(ipv6_addr, 993),
801                SocketAddr::new(ipv4_addr, 993)
802            ]
803        );
804        update_connection_history(
805            alice,
806            "imap",
807            "nine.testrun.org",
808            993,
809            "116.202.233.236",
810            now,
811        )
812        .await
813        .unwrap();
814        assert_eq!(
815            sort_by_connection_timestamp(
816                alice,
817                vec![
818                    SocketAddr::new(ipv6_addr, 993),
819                    SocketAddr::new(ipv4_addr, 993)
820                ],
821                "imap",
822                "nine.testrun.org"
823            )
824            .await
825            .unwrap(),
826            vec![
827                SocketAddr::new(ipv4_addr, 993),
828                SocketAddr::new(ipv6_addr, 993),
829            ]
830        );
831
832        assert_eq!(
833            sort_by_connection_timestamp(
834                alice,
835                vec![
836                    SocketAddr::new(ipv6_addr, 465),
837                    SocketAddr::new(ipv4_addr, 465)
838                ],
839                "smtp",
840                "nine.testrun.org"
841            )
842            .await
843            .unwrap(),
844            vec![
845                SocketAddr::new(ipv6_addr, 465),
846                SocketAddr::new(ipv4_addr, 465),
847            ]
848        );
849        update_connection_history(
850            alice,
851            "smtp",
852            "nine.testrun.org",
853            465,
854            "116.202.233.236",
855            now,
856        )
857        .await
858        .unwrap();
859        assert_eq!(
860            sort_by_connection_timestamp(
861                alice,
862                vec![
863                    SocketAddr::new(ipv6_addr, 465),
864                    SocketAddr::new(ipv4_addr, 465)
865                ],
866                "smtp",
867                "nine.testrun.org"
868            )
869            .await
870            .unwrap(),
871            vec![
872                SocketAddr::new(ipv4_addr, 465),
873                SocketAddr::new(ipv6_addr, 465),
874            ]
875        );
876
877        update_connection_history(
878            alice,
879            "imap",
880            "nine.testrun.org",
881            993,
882            "2a01:4f8:241:4ce8::2",
883            now,
884        )
885        .await
886        .unwrap();
887        assert_eq!(
888            sort_by_connection_timestamp(
889                alice,
890                vec![
891                    SocketAddr::new(ipv6_addr, 993),
892                    SocketAddr::new(ipv4_addr, 993)
893                ],
894                "imap",
895                "nine.testrun.org"
896            )
897            .await
898            .unwrap(),
899            vec![
900                SocketAddr::new(ipv6_addr, 993),
901                SocketAddr::new(ipv4_addr, 993)
902            ]
903        );
904    }
905
906    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
907    async fn test_lookup_cache() {
908        let alice = &TestContext::new_alice().await;
909
910        let ipv4_addr = IpAddr::V4(Ipv4Addr::new(116, 202, 233, 236));
911        let ipv6_addr = IpAddr::V6(Ipv6Addr::new(0x2a01, 0x4f8, 0x241, 0x4ce8, 0, 0, 0, 2));
912
913        let now = time();
914        assert!(lookup_cache(alice, "nine.testrun.org", 587, "smtp", now)
915            .await
916            .unwrap()
917            .is_empty());
918
919        update_cache(alice, "nine.testrun.org", "116.202.233.236", now)
920            .await
921            .unwrap();
922
923        assert_eq!(
924            lookup_cache(alice, "nine.testrun.org", 587, "smtp", now)
925                .await
926                .unwrap(),
927            vec![SocketAddr::new(ipv4_addr, 587)]
928        );
929
930        // Cache should be returned for other ports and no ALPN as well,
931        // port and ALPN should only affect the order
932        assert_eq!(
933            lookup_cache(alice, "nine.testrun.org", 443, "", now)
934                .await
935                .unwrap(),
936            vec![SocketAddr::new(ipv4_addr, 443)]
937        );
938
939        update_cache(alice, "nine.testrun.org", "2a01:4f8:241:4ce8::2", now + 30)
940            .await
941            .unwrap();
942
943        // New DNS cache entry should go first.
944        assert_eq!(
945            lookup_cache(alice, "nine.testrun.org", 443, "", now + 60)
946                .await
947                .unwrap(),
948            vec![
949                SocketAddr::new(ipv6_addr, 443),
950                SocketAddr::new(ipv4_addr, 443)
951            ],
952        );
953
954        // After successful connection to SMTP over port 465 using IPv4 address,
955        // IPv4 address has higher priority.
956        update_connection_history(
957            alice,
958            "smtp",
959            "nine.testrun.org",
960            465,
961            "116.202.233.236",
962            now + 100,
963        )
964        .await
965        .unwrap();
966        assert_eq!(
967            lookup_cache(alice, "nine.testrun.org", 465, "smtp", now + 120)
968                .await
969                .unwrap(),
970            vec![
971                SocketAddr::new(ipv4_addr, 465),
972                SocketAddr::new(ipv6_addr, 465)
973            ]
974        );
975
976        // For other ports and ALPNs order remains the same.
977        assert_eq!(
978            lookup_cache(alice, "nine.testrun.org", 993, "imap", now + 120)
979                .await
980                .unwrap(),
981            vec![
982                SocketAddr::new(ipv6_addr, 993),
983                SocketAddr::new(ipv4_addr, 993)
984            ],
985        );
986        assert_eq!(
987            lookup_cache(alice, "nine.testrun.org", 465, "imap", now + 120)
988                .await
989                .unwrap(),
990            vec![
991                SocketAddr::new(ipv6_addr, 465),
992                SocketAddr::new(ipv4_addr, 465)
993            ],
994        );
995        assert_eq!(
996            lookup_cache(alice, "nine.testrun.org", 993, "smtp", now + 120)
997                .await
998                .unwrap(),
999            vec![
1000                SocketAddr::new(ipv6_addr, 993),
1001                SocketAddr::new(ipv4_addr, 993)
1002            ],
1003        );
1004    }
1005
1006    #[test]
1007    fn test_merge_with_cache() {
1008        let first_addr = IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1));
1009        let second_addr = IpAddr::V4(Ipv4Addr::new(192, 168, 1, 2));
1010
1011        // If there is no cache, just return resolved addresses.
1012        {
1013            let resolved_addrs = vec![
1014                SocketAddr::new(first_addr, 993),
1015                SocketAddr::new(second_addr, 993),
1016            ];
1017            let cache = vec![];
1018            assert_eq!(
1019                merge_with_cache(resolved_addrs.clone(), cache),
1020                resolved_addrs
1021            );
1022        }
1023
1024        // If cache contains address that is not in resolution results,
1025        // it is inserted in the merged result.
1026        {
1027            let resolved_addrs = vec![SocketAddr::new(first_addr, 993)];
1028            let cache = vec![SocketAddr::new(second_addr, 993)];
1029            assert_eq!(
1030                merge_with_cache(resolved_addrs, cache),
1031                vec![
1032                    SocketAddr::new(first_addr, 993),
1033                    SocketAddr::new(second_addr, 993),
1034                ]
1035            );
1036        }
1037
1038        // If cache contains address that is already in resolution results,
1039        // it is not duplicated.
1040        {
1041            let resolved_addrs = vec![
1042                SocketAddr::new(first_addr, 993),
1043                SocketAddr::new(second_addr, 993),
1044            ];
1045            let cache = vec![SocketAddr::new(second_addr, 993)];
1046            assert_eq!(
1047                merge_with_cache(resolved_addrs, cache),
1048                vec![
1049                    SocketAddr::new(first_addr, 993),
1050                    SocketAddr::new(second_addr, 993),
1051                ]
1052            );
1053        }
1054
1055        // If DNS resolvers returns a lot of results,
1056        // we should try cached results before going through all
1057        // the resolver results.
1058        {
1059            let resolved_addrs = vec![
1060                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 993),
1061                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), 993),
1062                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 3)), 993),
1063                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 4)), 993),
1064                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 5)), 993),
1065                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 6)), 993),
1066                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 7)), 993),
1067                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 8)), 993),
1068                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 9)), 993),
1069            ];
1070            let cache = vec![SocketAddr::new(second_addr, 993)];
1071            assert_eq!(
1072                merge_with_cache(resolved_addrs, cache),
1073                vec![
1074                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 993),
1075                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), 993),
1076                    SocketAddr::new(second_addr, 993),
1077                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 3)), 993),
1078                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 4)), 993),
1079                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 5)), 993),
1080                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 6)), 993),
1081                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 7)), 993),
1082                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 8)), 993),
1083                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 9)), 993),
1084                ]
1085            );
1086        }
1087
1088        // Even if cache already contains all the incorrect results
1089        // that resolver returns, this should not result in them being sorted to the top.
1090        // Cache has known to work result returned first,
1091        // so we should try it after the second result.
1092        {
1093            let resolved_addrs = vec![
1094                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 993),
1095                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), 993),
1096                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 3)), 993),
1097                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 4)), 993),
1098                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 5)), 993),
1099                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 6)), 993),
1100                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 7)), 993),
1101                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 8)), 993),
1102                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 9)), 993),
1103            ];
1104            let cache = vec![
1105                SocketAddr::new(second_addr, 993),
1106                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 9)), 993),
1107                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 8)), 993),
1108                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 7)), 993),
1109                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 6)), 993),
1110                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 5)), 993),
1111                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 4)), 993),
1112                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 3)), 993),
1113                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), 993),
1114            ];
1115            assert_eq!(
1116                merge_with_cache(resolved_addrs, cache),
1117                vec![
1118                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 993),
1119                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), 993),
1120                    SocketAddr::new(second_addr, 993),
1121                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 9)), 993),
1122                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 8)), 993),
1123                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 7)), 993),
1124                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 6)), 993),
1125                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 5)), 993),
1126                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 4)), 993),
1127                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 3)), 993),
1128                ]
1129            );
1130        }
1131    }
1132}