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