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, ensure};
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            "mx.freenet.de",
511            vec![
512                IpAddr::V4(Ipv4Addr::new(194, 97, 208, 36)),
513                IpAddr::V4(Ipv4Addr::new(194, 97, 208, 34)),
514                IpAddr::V4(Ipv4Addr::new(194, 97, 208, 35)),
515                IpAddr::V4(Ipv4Addr::new(194, 97, 208, 39)),
516                IpAddr::V4(Ipv4Addr::new(194, 97, 208, 37)),
517                IpAddr::V4(Ipv4Addr::new(194, 97, 208, 38)),
518            ],
519        ),
520        (
521            "posteo.de",
522            vec![
523                IpAddr::V4(Ipv4Addr::new(185, 67, 36, 168)),
524                IpAddr::V4(Ipv4Addr::new(185, 67, 36, 169)),
525            ],
526        ),
527        (
528            "psmtp.schulon.org",
529            vec![IpAddr::V4(Ipv4Addr::new(194, 77, 246, 20))],
530        ),
531        (
532            "smtp.mail.de",
533            vec![IpAddr::V4(Ipv4Addr::new(62, 201, 172, 21))],
534        ),
535        (
536            "smtp.mail.ru",
537            vec![
538                IpAddr::V4(Ipv4Addr::new(94, 100, 180, 160)),
539                IpAddr::V4(Ipv4Addr::new(217, 69, 139, 160)),
540            ],
541        ),
542        (
543            "imap.mail.yahoo.com",
544            vec![
545                IpAddr::V4(Ipv4Addr::new(87, 248, 103, 8)),
546                IpAddr::V4(Ipv4Addr::new(212, 82, 101, 24)),
547            ],
548        ),
549        (
550            "smtp.mail.yahoo.com",
551            vec![IpAddr::V4(Ipv4Addr::new(87, 248, 97, 36))],
552        ),
553        (
554            "imap.mailo.com",
555            vec![IpAddr::V4(Ipv4Addr::new(213, 182, 54, 20))],
556        ),
557        (
558            "imap.migadu.com",
559            vec![
560                IpAddr::V4(Ipv4Addr::new(51, 210, 3, 23)),
561                IpAddr::V4(Ipv4Addr::new(51, 210, 3, 20)),
562            ],
563        ),
564        (
565            "smtp.migadu.com",
566            vec![
567                IpAddr::V4(Ipv4Addr::new(37, 59, 57, 117)),
568                IpAddr::V4(Ipv4Addr::new(51, 255, 82, 75)),
569            ],
570        ),
571        (
572            "smtp.mailo.com",
573            vec![IpAddr::V4(Ipv4Addr::new(213, 182, 54, 20))],
574        ),
575        (
576            "smtp.naver.com",
577            vec![IpAddr::V4(Ipv4Addr::new(125, 209, 238, 155))],
578        ),
579        (
580            "smtp.ouvaton.coop",
581            vec![IpAddr::V4(Ipv4Addr::new(194, 36, 166, 20))],
582        ),
583        (
584            "smtp.purelymail.com",
585            vec![IpAddr::V4(Ipv4Addr::new(18, 204, 123, 63))],
586        ),
587        (
588            "imap.qq.com",
589            vec![
590                IpAddr::V4(Ipv4Addr::new(43, 163, 178, 76)),
591                IpAddr::V4(Ipv4Addr::new(43, 129, 255, 54)),
592            ],
593        ),
594        (
595            "smtp.qq.com",
596            vec![
597                IpAddr::V4(Ipv4Addr::new(43, 129, 255, 54)),
598                IpAddr::V4(Ipv4Addr::new(43, 163, 178, 76)),
599            ],
600        ),
601        (
602            "imap.rambler.ru",
603            vec![
604                IpAddr::V4(Ipv4Addr::new(81, 19, 77, 168)),
605                IpAddr::V4(Ipv4Addr::new(81, 19, 77, 169)),
606                IpAddr::V4(Ipv4Addr::new(81, 19, 77, 170)),
607                IpAddr::V4(Ipv4Addr::new(81, 19, 77, 171)),
608            ],
609        ),
610        (
611            "smtp.rambler.ru",
612            vec![
613                IpAddr::V4(Ipv4Addr::new(81, 19, 77, 164)),
614                IpAddr::V4(Ipv4Addr::new(81, 19, 77, 165)),
615                IpAddr::V4(Ipv4Addr::new(81, 19, 77, 166)),
616                IpAddr::V4(Ipv4Addr::new(81, 19, 77, 167)),
617            ],
618        ),
619        (
620            "stinpriza.net",
621            vec![IpAddr::V4(Ipv4Addr::new(5, 9, 122, 184))],
622        ),
623        (
624            "webbox222.server-home.org",
625            vec![IpAddr::V4(Ipv4Addr::new(91, 203, 111, 88))],
626        ),
627        (
628            "undernet.uy",
629            vec![IpAddr::V4(Ipv4Addr::new(200, 40, 115, 74))],
630        ),
631        (
632            "imap.vivaldi.net",
633            vec![IpAddr::V4(Ipv4Addr::new(31, 209, 137, 15))],
634        ),
635        (
636            "smtp.vivaldi.net",
637            vec![IpAddr::V4(Ipv4Addr::new(31, 209, 137, 12))],
638        ),
639        (
640            "imap.vodafonemail.de",
641            vec![
642                IpAddr::V4(Ipv4Addr::new(178, 15, 69, 210)),
643                IpAddr::V4(Ipv4Addr::new(151, 189, 176, 206)),
644            ],
645        ),
646        (
647            "smtp.vodafonemail.de",
648            vec![
649                IpAddr::V4(Ipv4Addr::new(151, 189, 176, 206)),
650                IpAddr::V4(Ipv4Addr::new(178, 15, 69, 206)),
651            ],
652        ),
653        (
654            "smtp.web.de",
655            vec![
656                IpAddr::V4(Ipv4Addr::new(213, 165, 67, 124)),
657                IpAddr::V4(Ipv4Addr::new(213, 165, 67, 108)),
658            ],
659        ),
660        (
661            "imap.yandex.com",
662            vec![IpAddr::V4(Ipv4Addr::new(77, 88, 21, 125))],
663        ),
664        (
665            "smtp.yandex.com",
666            vec![IpAddr::V4(Ipv4Addr::new(77, 88, 21, 158))],
667        ),
668        (
669            "smtp.ziggo.nl",
670            vec![IpAddr::V4(Ipv4Addr::new(84, 116, 6, 3))],
671        ),
672        (
673            "smtp.zoho.eu",
674            vec![
675                IpAddr::V4(Ipv4Addr::new(185, 230, 212, 164)),
676                IpAddr::V4(Ipv4Addr::new(185, 230, 214, 164)),
677            ],
678        ),
679        // Known public chatmail relays from https://chatmail.at/relays
680        (
681            "mehl.cloud",
682            vec![IpAddr::V4(Ipv4Addr::new(95, 217, 223, 172))],
683        ),
684        (
685            "mailchat.pl",
686            vec![IpAddr::V4(Ipv4Addr::new(46, 62, 144, 137))],
687        ),
688        (
689            "chatmail.woodpeckersnest.space",
690            vec![IpAddr::V4(Ipv4Addr::new(85, 215, 162, 146))],
691        ),
692        (
693            "chatmail.culturanerd.it",
694            vec![IpAddr::V4(Ipv4Addr::new(82, 165, 94, 165))],
695        ),
696        (
697            "chatmail.hackea.org",
698            vec![IpAddr::V4(Ipv4Addr::new(82, 165, 11, 85))],
699        ),
700        (
701            "chika.aangat.lahat.computer",
702            vec![IpAddr::V4(Ipv4Addr::new(71, 19, 150, 113))],
703        ),
704        (
705            "tarpit.fun",
706            vec![IpAddr::V4(Ipv4Addr::new(152, 53, 86, 246))],
707        ),
708        (
709            "d.gaufr.es",
710            vec![IpAddr::V4(Ipv4Addr::new(51, 77, 140, 91))],
711        ),
712        (
713            "chtml.ca",
714            vec![IpAddr::V4(Ipv4Addr::new(51, 222, 156, 177))],
715        ),
716        (
717            "chatmail.au",
718            vec![IpAddr::V4(Ipv4Addr::new(45, 124, 54, 79))],
719        ),
720        (
721            "sombras.chat",
722            vec![IpAddr::V4(Ipv4Addr::new(82, 25, 70, 154))],
723        ),
724        (
725            "e2ee.wang",
726            vec![IpAddr::V4(Ipv4Addr::new(139, 84, 233, 161))],
727        ),
728        (
729            "chat.privittytech.com",
730            vec![IpAddr::V4(Ipv4Addr::new(35, 154, 144, 0))],
731        ),
732        ("e2ee.im", vec![IpAddr::V4(Ipv4Addr::new(45, 137, 99, 57))]),
733        (
734            "chatmail.email",
735            vec![IpAddr::V4(Ipv4Addr::new(57, 128, 220, 120))],
736        ),
737        (
738            "danneskjold.de",
739            vec![IpAddr::V4(Ipv4Addr::new(46, 62, 216, 132))],
740        ),
741        (
742            "darkrun.dev",
743            vec![IpAddr::V4(Ipv4Addr::new(72, 11, 149, 146))],
744        ),
745    ])
746});
747
748async fn lookup_cache(
749    context: &Context,
750    host: &str,
751    port: u16,
752    alpn: &str,
753    now: i64,
754) -> Result<Vec<SocketAddr>> {
755    let mut res = Vec::new();
756    for cached_address in context
757        .sql
758        .query_map_vec(
759            "SELECT dns_cache.address
760             FROM dns_cache
761             LEFT JOIN connection_history
762               ON dns_cache.hostname = connection_history.host
763               AND dns_cache.address = connection_history.addr
764               AND connection_history.port = ?
765               AND connection_history.alpn = ?
766             WHERE dns_cache.hostname = ?
767             AND ? < dns_cache.timestamp + ?
768             ORDER BY IFNULL(connection_history.timestamp, dns_cache.timestamp) DESC
769             LIMIT 50",
770            (port, alpn, host, now, super::CACHE_TTL),
771            |row| {
772                let address: String = row.get(0)?;
773                Ok(address)
774            },
775        )
776        .await?
777    {
778        match IpAddr::from_str(&cached_address) {
779            Ok(ip_addr) => {
780                let addr = SocketAddr::new(ip_addr, port);
781                res.push(addr);
782            }
783            Err(err) => {
784                warn!(
785                    context,
786                    "Failed to parse cached address {:?}: {:#}.", cached_address, err
787                );
788            }
789        }
790    }
791    Ok(res)
792}
793
794/// Sorts DNS resolution results by connection timestamp in descending order
795/// so IP addresses that we recently connected to successfully are tried first.
796async fn sort_by_connection_timestamp(
797    context: &Context,
798    input: Vec<SocketAddr>,
799    alpn: &str,
800    host: &str,
801) -> Result<Vec<SocketAddr>> {
802    let mut res: Vec<(Option<i64>, SocketAddr)> = Vec::with_capacity(input.len());
803    for addr in input {
804        let timestamp = load_connection_timestamp(
805            &context.sql,
806            alpn,
807            host,
808            addr.port(),
809            Some(&addr.ip().to_string()),
810        )
811        .await?;
812        res.push((timestamp, addr));
813    }
814    res.sort_by_key(|(ts, _addr)| std::cmp::Reverse(*ts));
815    Ok(res.into_iter().map(|(_ts, addr)| addr).collect())
816}
817
818/// Looks up hostname and port using DNS and updates the address resolution cache.
819///
820/// `alpn` is used to sort DNS results by the time we have successfully
821/// connected to the IP address using given `alpn`.
822/// If result sorting is not needed or `alpn` is unknown,
823/// pass empty string here, e.g. for HTTP requests
824/// or when resolving the IP address of SOCKS proxy.
825///
826/// If `load_cache` is true, appends cached results not older than 30 days to the end
827/// or entries from fallback cache if there are no cached addresses.
828pub(crate) async fn lookup_host_with_cache(
829    context: &Context,
830    hostname: &str,
831    port: u16,
832    alpn: &str,
833    load_cache: bool,
834) -> Result<Vec<SocketAddr>> {
835    let now = time();
836    let resolved_addrs = match lookup_host_and_update_cache(context, hostname, port, now).await {
837        Ok(res) => {
838            if alpn.is_empty() {
839                res
840            } else {
841                sort_by_connection_timestamp(context, res, alpn, hostname).await?
842            }
843        }
844        Err(err) => {
845            warn!(
846                context,
847                "DNS resolution for {hostname}:{port} failed: {err:#}."
848            );
849            Vec::new()
850        }
851    };
852
853    let addrs = if load_cache {
854        let mut cache = lookup_cache(context, hostname, port, alpn, now).await?;
855        if let Some(ips) = DNS_PRELOAD.get(hostname) {
856            for ip in ips {
857                let addr = SocketAddr::new(*ip, port);
858                if !cache.contains(&addr) {
859                    cache.push(addr);
860                }
861            }
862        }
863
864        merge_with_cache(resolved_addrs, cache)
865    } else {
866        resolved_addrs
867    };
868    ensure!(
869        !addrs.is_empty(),
870        "Could not find DNS resolutions for {hostname}:{port}. Check server hostname and your network"
871    );
872    Ok(addrs)
873}
874
875/// Merges results received from DNS with cached results.
876///
877/// At most 10 results are returned.
878fn merge_with_cache(
879    mut resolved_addrs: Vec<SocketAddr>,
880    cache: Vec<SocketAddr>,
881) -> Vec<SocketAddr> {
882    let rest = resolved_addrs.split_off(std::cmp::min(resolved_addrs.len(), 2));
883
884    for addr in cache.into_iter().chain(rest.into_iter()) {
885        if !resolved_addrs.contains(&addr) {
886            resolved_addrs.push(addr);
887            if resolved_addrs.len() >= 10 {
888                break;
889            }
890        }
891    }
892
893    resolved_addrs
894}
895
896#[cfg(test)]
897mod tests {
898    use super::*;
899
900    use crate::net::update_connection_history;
901    use crate::test_utils::TestContext;
902
903    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
904    async fn test_sort_by_connection_timestamp() {
905        let alice = &TestContext::new_alice().await;
906        let now = time();
907
908        let ipv6_addr = IpAddr::V6(Ipv6Addr::new(0x2a01, 0x4f8, 0x241, 0x4ce8, 0, 0, 0, 2));
909        let ipv4_addr = IpAddr::V4(Ipv4Addr::new(116, 202, 233, 236));
910
911        assert_eq!(
912            sort_by_connection_timestamp(
913                alice,
914                vec![
915                    SocketAddr::new(ipv6_addr, 993),
916                    SocketAddr::new(ipv4_addr, 993)
917                ],
918                "imap",
919                "nine.testrun.org"
920            )
921            .await
922            .unwrap(),
923            vec![
924                SocketAddr::new(ipv6_addr, 993),
925                SocketAddr::new(ipv4_addr, 993)
926            ]
927        );
928        update_connection_history(
929            alice,
930            "imap",
931            "nine.testrun.org",
932            993,
933            "116.202.233.236",
934            now,
935        )
936        .await
937        .unwrap();
938        assert_eq!(
939            sort_by_connection_timestamp(
940                alice,
941                vec![
942                    SocketAddr::new(ipv6_addr, 993),
943                    SocketAddr::new(ipv4_addr, 993)
944                ],
945                "imap",
946                "nine.testrun.org"
947            )
948            .await
949            .unwrap(),
950            vec![
951                SocketAddr::new(ipv4_addr, 993),
952                SocketAddr::new(ipv6_addr, 993),
953            ]
954        );
955
956        assert_eq!(
957            sort_by_connection_timestamp(
958                alice,
959                vec![
960                    SocketAddr::new(ipv6_addr, 465),
961                    SocketAddr::new(ipv4_addr, 465)
962                ],
963                "smtp",
964                "nine.testrun.org"
965            )
966            .await
967            .unwrap(),
968            vec![
969                SocketAddr::new(ipv6_addr, 465),
970                SocketAddr::new(ipv4_addr, 465),
971            ]
972        );
973        update_connection_history(
974            alice,
975            "smtp",
976            "nine.testrun.org",
977            465,
978            "116.202.233.236",
979            now,
980        )
981        .await
982        .unwrap();
983        assert_eq!(
984            sort_by_connection_timestamp(
985                alice,
986                vec![
987                    SocketAddr::new(ipv6_addr, 465),
988                    SocketAddr::new(ipv4_addr, 465)
989                ],
990                "smtp",
991                "nine.testrun.org"
992            )
993            .await
994            .unwrap(),
995            vec![
996                SocketAddr::new(ipv4_addr, 465),
997                SocketAddr::new(ipv6_addr, 465),
998            ]
999        );
1000
1001        update_connection_history(
1002            alice,
1003            "imap",
1004            "nine.testrun.org",
1005            993,
1006            "2a01:4f8:241:4ce8::2",
1007            now,
1008        )
1009        .await
1010        .unwrap();
1011        assert_eq!(
1012            sort_by_connection_timestamp(
1013                alice,
1014                vec![
1015                    SocketAddr::new(ipv6_addr, 993),
1016                    SocketAddr::new(ipv4_addr, 993)
1017                ],
1018                "imap",
1019                "nine.testrun.org"
1020            )
1021            .await
1022            .unwrap(),
1023            vec![
1024                SocketAddr::new(ipv6_addr, 993),
1025                SocketAddr::new(ipv4_addr, 993)
1026            ]
1027        );
1028    }
1029
1030    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
1031    async fn test_lookup_cache() {
1032        let alice = &TestContext::new_alice().await;
1033
1034        let ipv4_addr = IpAddr::V4(Ipv4Addr::new(116, 202, 233, 236));
1035        let ipv6_addr = IpAddr::V6(Ipv6Addr::new(0x2a01, 0x4f8, 0x241, 0x4ce8, 0, 0, 0, 2));
1036
1037        let now = time();
1038        assert!(
1039            lookup_cache(alice, "nine.testrun.org", 587, "smtp", now)
1040                .await
1041                .unwrap()
1042                .is_empty()
1043        );
1044
1045        update_cache(alice, "nine.testrun.org", "116.202.233.236", now)
1046            .await
1047            .unwrap();
1048
1049        assert_eq!(
1050            lookup_cache(alice, "nine.testrun.org", 587, "smtp", now)
1051                .await
1052                .unwrap(),
1053            vec![SocketAddr::new(ipv4_addr, 587)]
1054        );
1055
1056        // Cache should be returned for other ports and no ALPN as well,
1057        // port and ALPN should only affect the order
1058        assert_eq!(
1059            lookup_cache(alice, "nine.testrun.org", 443, "", now)
1060                .await
1061                .unwrap(),
1062            vec![SocketAddr::new(ipv4_addr, 443)]
1063        );
1064
1065        update_cache(alice, "nine.testrun.org", "2a01:4f8:241:4ce8::2", now + 30)
1066            .await
1067            .unwrap();
1068
1069        // New DNS cache entry should go first.
1070        assert_eq!(
1071            lookup_cache(alice, "nine.testrun.org", 443, "", now + 60)
1072                .await
1073                .unwrap(),
1074            vec![
1075                SocketAddr::new(ipv6_addr, 443),
1076                SocketAddr::new(ipv4_addr, 443)
1077            ],
1078        );
1079
1080        // After successful connection to SMTP over port 465 using IPv4 address,
1081        // IPv4 address has higher priority.
1082        update_connection_history(
1083            alice,
1084            "smtp",
1085            "nine.testrun.org",
1086            465,
1087            "116.202.233.236",
1088            now + 100,
1089        )
1090        .await
1091        .unwrap();
1092        assert_eq!(
1093            lookup_cache(alice, "nine.testrun.org", 465, "smtp", now + 120)
1094                .await
1095                .unwrap(),
1096            vec![
1097                SocketAddr::new(ipv4_addr, 465),
1098                SocketAddr::new(ipv6_addr, 465)
1099            ]
1100        );
1101
1102        // For other ports and ALPNs order remains the same.
1103        assert_eq!(
1104            lookup_cache(alice, "nine.testrun.org", 993, "imap", now + 120)
1105                .await
1106                .unwrap(),
1107            vec![
1108                SocketAddr::new(ipv6_addr, 993),
1109                SocketAddr::new(ipv4_addr, 993)
1110            ],
1111        );
1112        assert_eq!(
1113            lookup_cache(alice, "nine.testrun.org", 465, "imap", now + 120)
1114                .await
1115                .unwrap(),
1116            vec![
1117                SocketAddr::new(ipv6_addr, 465),
1118                SocketAddr::new(ipv4_addr, 465)
1119            ],
1120        );
1121        assert_eq!(
1122            lookup_cache(alice, "nine.testrun.org", 993, "smtp", now + 120)
1123                .await
1124                .unwrap(),
1125            vec![
1126                SocketAddr::new(ipv6_addr, 993),
1127                SocketAddr::new(ipv4_addr, 993)
1128            ],
1129        );
1130    }
1131
1132    #[test]
1133    fn test_merge_with_cache() {
1134        let first_addr = IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1));
1135        let second_addr = IpAddr::V4(Ipv4Addr::new(192, 168, 1, 2));
1136
1137        // If there is no cache, just return resolved addresses.
1138        {
1139            let resolved_addrs = vec![
1140                SocketAddr::new(first_addr, 993),
1141                SocketAddr::new(second_addr, 993),
1142            ];
1143            let cache = vec![];
1144            assert_eq!(
1145                merge_with_cache(resolved_addrs.clone(), cache),
1146                resolved_addrs
1147            );
1148        }
1149
1150        // If cache contains address that is not in resolution results,
1151        // it is inserted in the merged result.
1152        {
1153            let resolved_addrs = vec![SocketAddr::new(first_addr, 993)];
1154            let cache = vec![SocketAddr::new(second_addr, 993)];
1155            assert_eq!(
1156                merge_with_cache(resolved_addrs, cache),
1157                vec![
1158                    SocketAddr::new(first_addr, 993),
1159                    SocketAddr::new(second_addr, 993),
1160                ]
1161            );
1162        }
1163
1164        // If cache contains address that is already in resolution results,
1165        // it is not duplicated.
1166        {
1167            let resolved_addrs = vec![
1168                SocketAddr::new(first_addr, 993),
1169                SocketAddr::new(second_addr, 993),
1170            ];
1171            let cache = vec![SocketAddr::new(second_addr, 993)];
1172            assert_eq!(
1173                merge_with_cache(resolved_addrs, cache),
1174                vec![
1175                    SocketAddr::new(first_addr, 993),
1176                    SocketAddr::new(second_addr, 993),
1177                ]
1178            );
1179        }
1180
1181        // If DNS resolvers returns a lot of results,
1182        // we should try cached results before going through all
1183        // the resolver results.
1184        {
1185            let resolved_addrs = vec![
1186                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 993),
1187                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), 993),
1188                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 3)), 993),
1189                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 4)), 993),
1190                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 5)), 993),
1191                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 6)), 993),
1192                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 7)), 993),
1193                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 8)), 993),
1194                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 9)), 993),
1195            ];
1196            let cache = vec![SocketAddr::new(second_addr, 993)];
1197            assert_eq!(
1198                merge_with_cache(resolved_addrs, cache),
1199                vec![
1200                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 993),
1201                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), 993),
1202                    SocketAddr::new(second_addr, 993),
1203                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 3)), 993),
1204                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 4)), 993),
1205                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 5)), 993),
1206                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 6)), 993),
1207                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 7)), 993),
1208                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 8)), 993),
1209                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 9)), 993),
1210                ]
1211            );
1212        }
1213
1214        // Even if cache already contains all the incorrect results
1215        // that resolver returns, this should not result in them being sorted to the top.
1216        // Cache has known to work result returned first,
1217        // so we should try it after the second result.
1218        {
1219            let resolved_addrs = vec![
1220                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 993),
1221                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), 993),
1222                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 3)), 993),
1223                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 4)), 993),
1224                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 5)), 993),
1225                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 6)), 993),
1226                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 7)), 993),
1227                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 8)), 993),
1228                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 9)), 993),
1229            ];
1230            let cache = vec![
1231                SocketAddr::new(second_addr, 993),
1232                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 9)), 993),
1233                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 8)), 993),
1234                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 7)), 993),
1235                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 6)), 993),
1236                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 5)), 993),
1237                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 4)), 993),
1238                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 3)), 993),
1239                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), 993),
1240            ];
1241            assert_eq!(
1242                merge_with_cache(resolved_addrs, cache),
1243                vec![
1244                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 993),
1245                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), 993),
1246                    SocketAddr::new(second_addr, 993),
1247                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 9)), 993),
1248                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 8)), 993),
1249                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 7)), 993),
1250                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 6)), 993),
1251                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 5)), 993),
1252                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 4)), 993),
1253                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 3)), 993),
1254                ]
1255            );
1256        }
1257    }
1258}