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