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.
230static DNS_PRELOAD: LazyLock<HashMap<&'static str, Vec<IpAddr>>> = LazyLock::new(|| {
231    HashMap::from([
232        (
233            "mail.sangham.net",
234            vec![
235                IpAddr::V4(Ipv4Addr::new(159, 69, 186, 85)),
236                IpAddr::V6(Ipv6Addr::new(0x2a01, 0x4f8, 0xc17, 0x798c, 0, 0, 0, 1)),
237            ],
238        ),
239        (
240            "nine.testrun.org",
241            vec![
242                IpAddr::V4(Ipv4Addr::new(116, 202, 233, 236)),
243                IpAddr::V4(Ipv4Addr::new(128, 140, 126, 197)),
244                IpAddr::V4(Ipv4Addr::new(49, 12, 116, 128)),
245                IpAddr::V6(Ipv6Addr::new(0x2a01, 0x4f8, 0x241, 0x4ce8, 0, 0, 0, 2)),
246            ],
247        ),
248        (
249            "disroot.org",
250            vec![IpAddr::V4(Ipv4Addr::new(178, 21, 23, 139))],
251        ),
252        (
253            "imap.gmail.com",
254            vec![
255                IpAddr::V4(Ipv4Addr::new(142, 250, 110, 108)),
256                IpAddr::V4(Ipv4Addr::new(142, 250, 110, 109)),
257                IpAddr::V4(Ipv4Addr::new(66, 102, 1, 108)),
258                IpAddr::V4(Ipv4Addr::new(66, 102, 1, 109)),
259                IpAddr::V6(Ipv6Addr::new(0x2a00, 0x1450, 0x400c, 0xc1f, 0, 0, 0, 0x6c)),
260                IpAddr::V6(Ipv6Addr::new(0x2a00, 0x1450, 0x400c, 0xc1f, 0, 0, 0, 0x6d)),
261            ],
262        ),
263        (
264            "smtp.gmail.com",
265            vec![
266                IpAddr::V4(Ipv4Addr::new(142, 250, 110, 109)),
267                IpAddr::V6(Ipv6Addr::new(0x2a00, 0x1450, 0x4013, 0xc04, 0, 0, 0, 0x6c)),
268            ],
269        ),
270        (
271            "mail.autistici.org",
272            vec![
273                IpAddr::V4(Ipv4Addr::new(198, 167, 222, 108)),
274                IpAddr::V4(Ipv4Addr::new(82, 94, 249, 234)),
275                IpAddr::V4(Ipv4Addr::new(93, 190, 126, 19)),
276            ],
277        ),
278        (
279            "smtp.autistici.org",
280            vec![
281                IpAddr::V4(Ipv4Addr::new(198, 167, 222, 108)),
282                IpAddr::V4(Ipv4Addr::new(82, 94, 249, 234)),
283                IpAddr::V4(Ipv4Addr::new(93, 190, 126, 19)),
284            ],
285        ),
286        (
287            "daleth.cafe",
288            vec![IpAddr::V4(Ipv4Addr::new(37, 27, 6, 204))],
289        ),
290        (
291            "imap.163.com",
292            vec![IpAddr::V4(Ipv4Addr::new(111, 124, 203, 45))],
293        ),
294        (
295            "smtp.163.com",
296            vec![IpAddr::V4(Ipv4Addr::new(103, 129, 252, 45))],
297        ),
298        (
299            "imap.aol.com",
300            vec![
301                IpAddr::V4(Ipv4Addr::new(212, 82, 101, 33)),
302                IpAddr::V4(Ipv4Addr::new(87, 248, 98, 69)),
303            ],
304        ),
305        (
306            "smtp.aol.com",
307            vec![IpAddr::V4(Ipv4Addr::new(87, 248, 97, 31))],
308        ),
309        (
310            "mail.arcor.de",
311            vec![IpAddr::V4(Ipv4Addr::new(2, 207, 150, 234))],
312        ),
313        (
314            "imap.arcor.de",
315            vec![IpAddr::V4(Ipv4Addr::new(2, 207, 150, 230))],
316        ),
317        (
318            "imap.fastmail.com",
319            vec![
320                IpAddr::V4(Ipv4Addr::new(103, 168, 172, 43)),
321                IpAddr::V4(Ipv4Addr::new(103, 168, 172, 58)),
322            ],
323        ),
324        (
325            "smtp.fastmail.com",
326            vec![
327                IpAddr::V4(Ipv4Addr::new(103, 168, 172, 45)),
328                IpAddr::V4(Ipv4Addr::new(103, 168, 172, 60)),
329            ],
330        ),
331        (
332            "imap.gmx.net",
333            vec![
334                IpAddr::V4(Ipv4Addr::new(212, 227, 17, 170)),
335                IpAddr::V4(Ipv4Addr::new(212, 227, 17, 186)),
336            ],
337        ),
338        (
339            "imap.mail.de",
340            vec![IpAddr::V4(Ipv4Addr::new(62, 201, 172, 16))],
341        ),
342        (
343            "smtp.mailbox.org",
344            vec![IpAddr::V4(Ipv4Addr::new(185, 97, 174, 196))],
345        ),
346        (
347            "imap.mailbox.org",
348            vec![IpAddr::V4(Ipv4Addr::new(185, 97, 174, 199))],
349        ),
350        (
351            "imap.naver.com",
352            vec![IpAddr::V4(Ipv4Addr::new(125, 209, 238, 153))],
353        ),
354        (
355            "imap.ouvaton.coop",
356            vec![IpAddr::V4(Ipv4Addr::new(194, 36, 166, 20))],
357        ),
358        (
359            "imap.purelymail.com",
360            vec![IpAddr::V4(Ipv4Addr::new(18, 204, 123, 63))],
361        ),
362        (
363            "imap.tiscali.it",
364            vec![IpAddr::V4(Ipv4Addr::new(213, 205, 33, 10))],
365        ),
366        (
367            "smtp.tiscali.it",
368            vec![IpAddr::V4(Ipv4Addr::new(213, 205, 33, 13))],
369        ),
370        (
371            "imap.web.de",
372            vec![
373                IpAddr::V4(Ipv4Addr::new(212, 227, 17, 162)),
374                IpAddr::V4(Ipv4Addr::new(212, 227, 17, 178)),
375            ],
376        ),
377        (
378            "imap.ziggo.nl",
379            vec![IpAddr::V4(Ipv4Addr::new(84, 116, 6, 3))],
380        ),
381        (
382            "imap.zoho.eu",
383            vec![IpAddr::V4(Ipv4Addr::new(185, 230, 214, 25))],
384        ),
385        (
386            "imaps.bluewin.ch",
387            vec![
388                IpAddr::V4(Ipv4Addr::new(16, 62, 253, 42)),
389                IpAddr::V4(Ipv4Addr::new(16, 63, 141, 244)),
390                IpAddr::V4(Ipv4Addr::new(16, 63, 146, 183)),
391            ],
392        ),
393        (
394            "mail.buzon.uy",
395            vec![IpAddr::V4(Ipv4Addr::new(185, 101, 93, 79))],
396        ),
397        (
398            "mail.ecloud.global",
399            vec![IpAddr::V4(Ipv4Addr::new(95, 217, 246, 96))],
400        ),
401        (
402            "mail.ende.in.net",
403            vec![IpAddr::V4(Ipv4Addr::new(95, 217, 5, 72))],
404        ),
405        (
406            "mail.gmx.net",
407            vec![
408                IpAddr::V4(Ipv4Addr::new(212, 227, 17, 168)),
409                IpAddr::V4(Ipv4Addr::new(212, 227, 17, 190)),
410            ],
411        ),
412        (
413            "mail.infomaniak.com",
414            vec![
415                IpAddr::V4(Ipv4Addr::new(83, 166, 143, 44)),
416                IpAddr::V4(Ipv4Addr::new(83, 166, 143, 45)),
417            ],
418        ),
419        (
420            "mail.mymagenta.at",
421            vec![IpAddr::V4(Ipv4Addr::new(80, 109, 253, 241))],
422        ),
423        (
424            "mail.nubo.coop",
425            vec![IpAddr::V4(Ipv4Addr::new(79, 99, 201, 10))],
426        ),
427        (
428            "mail.riseup.net",
429            vec![
430                IpAddr::V4(Ipv4Addr::new(198, 252, 153, 70)),
431                IpAddr::V4(Ipv4Addr::new(198, 252, 153, 71)),
432            ],
433        ),
434        (
435            "mail.systemausfall.org",
436            vec![
437                IpAddr::V4(Ipv4Addr::new(51, 75, 71, 249)),
438                IpAddr::V4(Ipv4Addr::new(80, 153, 252, 42)),
439            ],
440        ),
441        (
442            "mail.systemli.org",
443            vec![IpAddr::V4(Ipv4Addr::new(93, 190, 126, 36))],
444        ),
445        (
446            "mehl.cloud",
447            vec![IpAddr::V4(Ipv4Addr::new(95, 217, 223, 172))],
448        ),
449        (
450            "mx.freenet.de",
451            vec![
452                IpAddr::V4(Ipv4Addr::new(195, 4, 92, 210)),
453                IpAddr::V4(Ipv4Addr::new(195, 4, 92, 211)),
454                IpAddr::V4(Ipv4Addr::new(195, 4, 92, 212)),
455                IpAddr::V4(Ipv4Addr::new(195, 4, 92, 213)),
456            ],
457        ),
458        (
459            "newyear.aktivix.org",
460            vec![IpAddr::V4(Ipv4Addr::new(162, 247, 75, 192))],
461        ),
462        (
463            "pimap.schulon.org",
464            vec![IpAddr::V4(Ipv4Addr::new(194, 77, 246, 20))],
465        ),
466        (
467            "posteo.de",
468            vec![
469                IpAddr::V4(Ipv4Addr::new(185, 67, 36, 168)),
470                IpAddr::V4(Ipv4Addr::new(185, 67, 36, 169)),
471            ],
472        ),
473        (
474            "psmtp.schulon.org",
475            vec![IpAddr::V4(Ipv4Addr::new(194, 77, 246, 20))],
476        ),
477        (
478            "secureimap.t-online.de",
479            vec![
480                IpAddr::V4(Ipv4Addr::new(194, 25, 134, 114)),
481                IpAddr::V4(Ipv4Addr::new(194, 25, 134, 115)),
482                IpAddr::V4(Ipv4Addr::new(194, 25, 134, 50)),
483                IpAddr::V4(Ipv4Addr::new(194, 25, 134, 51)),
484            ],
485        ),
486        (
487            "securesmtp.t-online.de",
488            vec![
489                IpAddr::V4(Ipv4Addr::new(194, 25, 134, 110)),
490                IpAddr::V4(Ipv4Addr::new(194, 25, 134, 46)),
491            ],
492        ),
493        (
494            "smtp.aliyun.com",
495            vec![IpAddr::V4(Ipv4Addr::new(47, 246, 136, 232))],
496        ),
497        (
498            "smtp.mail.de",
499            vec![IpAddr::V4(Ipv4Addr::new(62, 201, 172, 21))],
500        ),
501        (
502            "smtp.mail.ru",
503            vec![
504                IpAddr::V4(Ipv4Addr::new(217, 69, 139, 160)),
505                IpAddr::V4(Ipv4Addr::new(94, 100, 180, 160)),
506            ],
507        ),
508        (
509            "imap.mail.yahoo.com",
510            vec![
511                IpAddr::V4(Ipv4Addr::new(87, 248, 103, 8)),
512                IpAddr::V4(Ipv4Addr::new(212, 82, 101, 24)),
513            ],
514        ),
515        (
516            "smtp.mail.yahoo.com",
517            vec![IpAddr::V4(Ipv4Addr::new(87, 248, 97, 36))],
518        ),
519        (
520            "imap.mailo.com",
521            vec![IpAddr::V4(Ipv4Addr::new(213, 182, 54, 20))],
522        ),
523        (
524            "smtp.mailo.com",
525            vec![IpAddr::V4(Ipv4Addr::new(213, 182, 54, 20))],
526        ),
527        (
528            "smtp.naver.com",
529            vec![IpAddr::V4(Ipv4Addr::new(125, 209, 238, 155))],
530        ),
531        (
532            "smtp.ouvaton.coop",
533            vec![IpAddr::V4(Ipv4Addr::new(194, 36, 166, 20))],
534        ),
535        (
536            "smtp.purelymail.com",
537            vec![IpAddr::V4(Ipv4Addr::new(18, 204, 123, 63))],
538        ),
539        (
540            "imap.qq.com",
541            vec![IpAddr::V4(Ipv4Addr::new(43, 129, 255, 54))],
542        ),
543        (
544            "smtp.qq.com",
545            vec![IpAddr::V4(Ipv4Addr::new(43, 129, 255, 54))],
546        ),
547        (
548            "imap.rambler.ru",
549            vec![
550                IpAddr::V4(Ipv4Addr::new(81, 19, 77, 169)),
551                IpAddr::V4(Ipv4Addr::new(81, 19, 77, 171)),
552                IpAddr::V4(Ipv4Addr::new(81, 19, 77, 168)),
553                IpAddr::V4(Ipv4Addr::new(81, 19, 77, 170)),
554            ],
555        ),
556        (
557            "smtp.rambler.ru",
558            vec![
559                IpAddr::V4(Ipv4Addr::new(81, 19, 77, 165)),
560                IpAddr::V4(Ipv4Addr::new(81, 19, 77, 167)),
561                IpAddr::V4(Ipv4Addr::new(81, 19, 77, 166)),
562                IpAddr::V4(Ipv4Addr::new(81, 19, 77, 164)),
563            ],
564        ),
565        (
566            "imap.vivaldi.net",
567            vec![IpAddr::V4(Ipv4Addr::new(31, 209, 137, 15))],
568        ),
569        (
570            "smtp.vivaldi.net",
571            vec![IpAddr::V4(Ipv4Addr::new(31, 209, 137, 12))],
572        ),
573        (
574            "imap.vodafonemail.de",
575            vec![IpAddr::V4(Ipv4Addr::new(2, 207, 150, 230))],
576        ),
577        (
578            "smtp.vodafonemail.de",
579            vec![IpAddr::V4(Ipv4Addr::new(2, 207, 150, 234))],
580        ),
581        (
582            "smtp.web.de",
583            vec![
584                IpAddr::V4(Ipv4Addr::new(213, 165, 67, 108)),
585                IpAddr::V4(Ipv4Addr::new(213, 165, 67, 124)),
586            ],
587        ),
588        (
589            "imap.yandex.com",
590            vec![IpAddr::V4(Ipv4Addr::new(77, 88, 21, 125))],
591        ),
592        (
593            "smtp.yandex.com",
594            vec![IpAddr::V4(Ipv4Addr::new(77, 88, 21, 158))],
595        ),
596        (
597            "smtp.ziggo.nl",
598            vec![IpAddr::V4(Ipv4Addr::new(84, 116, 6, 3))],
599        ),
600        (
601            "smtp.zoho.eu",
602            vec![IpAddr::V4(Ipv4Addr::new(185, 230, 212, 164))],
603        ),
604        (
605            "smtpauths.bluewin.ch",
606            vec![IpAddr::V4(Ipv4Addr::new(195, 186, 120, 54))],
607        ),
608        (
609            "stinpriza.net",
610            vec![IpAddr::V4(Ipv4Addr::new(5, 9, 122, 184))],
611        ),
612        (
613            "undernet.uy",
614            vec![IpAddr::V4(Ipv4Addr::new(167, 62, 254, 153))],
615        ),
616        (
617            "webbox222.server-home.org",
618            vec![IpAddr::V4(Ipv4Addr::new(91, 203, 111, 88))],
619        ),
620    ])
621});
622
623async fn lookup_cache(
624    context: &Context,
625    host: &str,
626    port: u16,
627    alpn: &str,
628    now: i64,
629) -> Result<Vec<SocketAddr>> {
630    let mut res = Vec::new();
631    for cached_address in context
632        .sql
633        .query_map(
634            "SELECT dns_cache.address
635             FROM dns_cache
636             LEFT JOIN connection_history
637               ON dns_cache.hostname = connection_history.host
638               AND dns_cache.address = connection_history.addr
639               AND connection_history.port = ?
640               AND connection_history.alpn = ?
641             WHERE dns_cache.hostname = ?
642             AND ? < dns_cache.timestamp + ?
643             ORDER BY IFNULL(connection_history.timestamp, dns_cache.timestamp) DESC
644             LIMIT 50",
645            (port, alpn, host, now, super::CACHE_TTL),
646            |row| {
647                let address: String = row.get(0)?;
648                Ok(address)
649            },
650            |rows| {
651                rows.collect::<std::result::Result<Vec<String>, _>>()
652                    .map_err(Into::into)
653            },
654        )
655        .await?
656    {
657        match IpAddr::from_str(&cached_address) {
658            Ok(ip_addr) => {
659                let addr = SocketAddr::new(ip_addr, port);
660                res.push(addr);
661            }
662            Err(err) => {
663                warn!(
664                    context,
665                    "Failed to parse cached address {:?}: {:#}.", cached_address, err
666                );
667            }
668        }
669    }
670    Ok(res)
671}
672
673/// Sorts DNS resolution results by connection timestamp in descending order
674/// so IP addresses that we recently connected to successfully are tried first.
675async fn sort_by_connection_timestamp(
676    context: &Context,
677    input: Vec<SocketAddr>,
678    alpn: &str,
679    host: &str,
680) -> Result<Vec<SocketAddr>> {
681    let mut res: Vec<(Option<i64>, SocketAddr)> = Vec::with_capacity(input.len());
682    for addr in input {
683        let timestamp = load_connection_timestamp(
684            &context.sql,
685            alpn,
686            host,
687            addr.port(),
688            Some(&addr.ip().to_string()),
689        )
690        .await?;
691        res.push((timestamp, addr));
692    }
693    res.sort_by_key(|(ts, _addr)| std::cmp::Reverse(*ts));
694    Ok(res.into_iter().map(|(_ts, addr)| addr).collect())
695}
696
697/// Looks up hostname and port using DNS and updates the address resolution cache.
698///
699/// `alpn` is used to sort DNS results by the time we have successfully
700/// connected to the IP address using given `alpn`.
701/// If result sorting is not needed or `alpn` is unknown,
702/// pass empty string here, e.g. for HTTP requests
703/// or when resolving the IP address of SOCKS proxy.
704///
705/// If `load_cache` is true, appends cached results not older than 30 days to the end
706/// or entries from fallback cache if there are no cached addresses.
707pub(crate) async fn lookup_host_with_cache(
708    context: &Context,
709    hostname: &str,
710    port: u16,
711    alpn: &str,
712    load_cache: bool,
713) -> Result<Vec<SocketAddr>> {
714    let now = time();
715    let resolved_addrs = match lookup_host_and_update_cache(context, hostname, port, now).await {
716        Ok(res) => {
717            if alpn.is_empty() {
718                res
719            } else {
720                sort_by_connection_timestamp(context, res, alpn, hostname).await?
721            }
722        }
723        Err(err) => {
724            warn!(
725                context,
726                "DNS resolution for {hostname}:{port} failed: {err:#}."
727            );
728            Vec::new()
729        }
730    };
731
732    if load_cache {
733        let mut cache = lookup_cache(context, hostname, port, alpn, now).await?;
734        if let Some(ips) = DNS_PRELOAD.get(hostname) {
735            for ip in ips {
736                let addr = SocketAddr::new(*ip, port);
737                if !cache.contains(&addr) {
738                    cache.push(addr);
739                }
740            }
741        }
742
743        Ok(merge_with_cache(resolved_addrs, cache))
744    } else {
745        Ok(resolved_addrs)
746    }
747}
748
749/// Merges results received from DNS with cached results.
750///
751/// At most 10 results are returned.
752fn merge_with_cache(
753    mut resolved_addrs: Vec<SocketAddr>,
754    cache: Vec<SocketAddr>,
755) -> Vec<SocketAddr> {
756    let rest = resolved_addrs.split_off(std::cmp::min(resolved_addrs.len(), 2));
757
758    for addr in cache.into_iter().chain(rest.into_iter()) {
759        if !resolved_addrs.contains(&addr) {
760            resolved_addrs.push(addr);
761            if resolved_addrs.len() >= 10 {
762                break;
763            }
764        }
765    }
766
767    resolved_addrs
768}
769
770#[cfg(test)]
771mod tests {
772    use super::*;
773
774    use crate::net::update_connection_history;
775    use crate::test_utils::TestContext;
776
777    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
778    async fn test_sort_by_connection_timestamp() {
779        let alice = &TestContext::new_alice().await;
780        let now = time();
781
782        let ipv6_addr = IpAddr::V6(Ipv6Addr::new(0x2a01, 0x4f8, 0x241, 0x4ce8, 0, 0, 0, 2));
783        let ipv4_addr = IpAddr::V4(Ipv4Addr::new(116, 202, 233, 236));
784
785        assert_eq!(
786            sort_by_connection_timestamp(
787                alice,
788                vec![
789                    SocketAddr::new(ipv6_addr, 993),
790                    SocketAddr::new(ipv4_addr, 993)
791                ],
792                "imap",
793                "nine.testrun.org"
794            )
795            .await
796            .unwrap(),
797            vec![
798                SocketAddr::new(ipv6_addr, 993),
799                SocketAddr::new(ipv4_addr, 993)
800            ]
801        );
802        update_connection_history(
803            alice,
804            "imap",
805            "nine.testrun.org",
806            993,
807            "116.202.233.236",
808            now,
809        )
810        .await
811        .unwrap();
812        assert_eq!(
813            sort_by_connection_timestamp(
814                alice,
815                vec![
816                    SocketAddr::new(ipv6_addr, 993),
817                    SocketAddr::new(ipv4_addr, 993)
818                ],
819                "imap",
820                "nine.testrun.org"
821            )
822            .await
823            .unwrap(),
824            vec![
825                SocketAddr::new(ipv4_addr, 993),
826                SocketAddr::new(ipv6_addr, 993),
827            ]
828        );
829
830        assert_eq!(
831            sort_by_connection_timestamp(
832                alice,
833                vec![
834                    SocketAddr::new(ipv6_addr, 465),
835                    SocketAddr::new(ipv4_addr, 465)
836                ],
837                "smtp",
838                "nine.testrun.org"
839            )
840            .await
841            .unwrap(),
842            vec![
843                SocketAddr::new(ipv6_addr, 465),
844                SocketAddr::new(ipv4_addr, 465),
845            ]
846        );
847        update_connection_history(
848            alice,
849            "smtp",
850            "nine.testrun.org",
851            465,
852            "116.202.233.236",
853            now,
854        )
855        .await
856        .unwrap();
857        assert_eq!(
858            sort_by_connection_timestamp(
859                alice,
860                vec![
861                    SocketAddr::new(ipv6_addr, 465),
862                    SocketAddr::new(ipv4_addr, 465)
863                ],
864                "smtp",
865                "nine.testrun.org"
866            )
867            .await
868            .unwrap(),
869            vec![
870                SocketAddr::new(ipv4_addr, 465),
871                SocketAddr::new(ipv6_addr, 465),
872            ]
873        );
874
875        update_connection_history(
876            alice,
877            "imap",
878            "nine.testrun.org",
879            993,
880            "2a01:4f8:241:4ce8::2",
881            now,
882        )
883        .await
884        .unwrap();
885        assert_eq!(
886            sort_by_connection_timestamp(
887                alice,
888                vec![
889                    SocketAddr::new(ipv6_addr, 993),
890                    SocketAddr::new(ipv4_addr, 993)
891                ],
892                "imap",
893                "nine.testrun.org"
894            )
895            .await
896            .unwrap(),
897            vec![
898                SocketAddr::new(ipv6_addr, 993),
899                SocketAddr::new(ipv4_addr, 993)
900            ]
901        );
902    }
903
904    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
905    async fn test_lookup_cache() {
906        let alice = &TestContext::new_alice().await;
907
908        let ipv4_addr = IpAddr::V4(Ipv4Addr::new(116, 202, 233, 236));
909        let ipv6_addr = IpAddr::V6(Ipv6Addr::new(0x2a01, 0x4f8, 0x241, 0x4ce8, 0, 0, 0, 2));
910
911        let now = time();
912        assert!(
913            lookup_cache(alice, "nine.testrun.org", 587, "smtp", now)
914                .await
915                .unwrap()
916                .is_empty()
917        );
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}