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            "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    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        Ok(merge_with_cache(resolved_addrs, cache))
865    } else {
866        Ok(resolved_addrs)
867    }
868}
869
870/// Merges results received from DNS with cached results.
871///
872/// At most 10 results are returned.
873fn merge_with_cache(
874    mut resolved_addrs: Vec<SocketAddr>,
875    cache: Vec<SocketAddr>,
876) -> Vec<SocketAddr> {
877    let rest = resolved_addrs.split_off(std::cmp::min(resolved_addrs.len(), 2));
878
879    for addr in cache.into_iter().chain(rest.into_iter()) {
880        if !resolved_addrs.contains(&addr) {
881            resolved_addrs.push(addr);
882            if resolved_addrs.len() >= 10 {
883                break;
884            }
885        }
886    }
887
888    resolved_addrs
889}
890
891#[cfg(test)]
892mod tests {
893    use super::*;
894
895    use crate::net::update_connection_history;
896    use crate::test_utils::TestContext;
897
898    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
899    async fn test_sort_by_connection_timestamp() {
900        let alice = &TestContext::new_alice().await;
901        let now = time();
902
903        let ipv6_addr = IpAddr::V6(Ipv6Addr::new(0x2a01, 0x4f8, 0x241, 0x4ce8, 0, 0, 0, 2));
904        let ipv4_addr = IpAddr::V4(Ipv4Addr::new(116, 202, 233, 236));
905
906        assert_eq!(
907            sort_by_connection_timestamp(
908                alice,
909                vec![
910                    SocketAddr::new(ipv6_addr, 993),
911                    SocketAddr::new(ipv4_addr, 993)
912                ],
913                "imap",
914                "nine.testrun.org"
915            )
916            .await
917            .unwrap(),
918            vec![
919                SocketAddr::new(ipv6_addr, 993),
920                SocketAddr::new(ipv4_addr, 993)
921            ]
922        );
923        update_connection_history(
924            alice,
925            "imap",
926            "nine.testrun.org",
927            993,
928            "116.202.233.236",
929            now,
930        )
931        .await
932        .unwrap();
933        assert_eq!(
934            sort_by_connection_timestamp(
935                alice,
936                vec![
937                    SocketAddr::new(ipv6_addr, 993),
938                    SocketAddr::new(ipv4_addr, 993)
939                ],
940                "imap",
941                "nine.testrun.org"
942            )
943            .await
944            .unwrap(),
945            vec![
946                SocketAddr::new(ipv4_addr, 993),
947                SocketAddr::new(ipv6_addr, 993),
948            ]
949        );
950
951        assert_eq!(
952            sort_by_connection_timestamp(
953                alice,
954                vec![
955                    SocketAddr::new(ipv6_addr, 465),
956                    SocketAddr::new(ipv4_addr, 465)
957                ],
958                "smtp",
959                "nine.testrun.org"
960            )
961            .await
962            .unwrap(),
963            vec![
964                SocketAddr::new(ipv6_addr, 465),
965                SocketAddr::new(ipv4_addr, 465),
966            ]
967        );
968        update_connection_history(
969            alice,
970            "smtp",
971            "nine.testrun.org",
972            465,
973            "116.202.233.236",
974            now,
975        )
976        .await
977        .unwrap();
978        assert_eq!(
979            sort_by_connection_timestamp(
980                alice,
981                vec![
982                    SocketAddr::new(ipv6_addr, 465),
983                    SocketAddr::new(ipv4_addr, 465)
984                ],
985                "smtp",
986                "nine.testrun.org"
987            )
988            .await
989            .unwrap(),
990            vec![
991                SocketAddr::new(ipv4_addr, 465),
992                SocketAddr::new(ipv6_addr, 465),
993            ]
994        );
995
996        update_connection_history(
997            alice,
998            "imap",
999            "nine.testrun.org",
1000            993,
1001            "2a01:4f8:241:4ce8::2",
1002            now,
1003        )
1004        .await
1005        .unwrap();
1006        assert_eq!(
1007            sort_by_connection_timestamp(
1008                alice,
1009                vec![
1010                    SocketAddr::new(ipv6_addr, 993),
1011                    SocketAddr::new(ipv4_addr, 993)
1012                ],
1013                "imap",
1014                "nine.testrun.org"
1015            )
1016            .await
1017            .unwrap(),
1018            vec![
1019                SocketAddr::new(ipv6_addr, 993),
1020                SocketAddr::new(ipv4_addr, 993)
1021            ]
1022        );
1023    }
1024
1025    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
1026    async fn test_lookup_cache() {
1027        let alice = &TestContext::new_alice().await;
1028
1029        let ipv4_addr = IpAddr::V4(Ipv4Addr::new(116, 202, 233, 236));
1030        let ipv6_addr = IpAddr::V6(Ipv6Addr::new(0x2a01, 0x4f8, 0x241, 0x4ce8, 0, 0, 0, 2));
1031
1032        let now = time();
1033        assert!(
1034            lookup_cache(alice, "nine.testrun.org", 587, "smtp", now)
1035                .await
1036                .unwrap()
1037                .is_empty()
1038        );
1039
1040        update_cache(alice, "nine.testrun.org", "116.202.233.236", now)
1041            .await
1042            .unwrap();
1043
1044        assert_eq!(
1045            lookup_cache(alice, "nine.testrun.org", 587, "smtp", now)
1046                .await
1047                .unwrap(),
1048            vec![SocketAddr::new(ipv4_addr, 587)]
1049        );
1050
1051        // Cache should be returned for other ports and no ALPN as well,
1052        // port and ALPN should only affect the order
1053        assert_eq!(
1054            lookup_cache(alice, "nine.testrun.org", 443, "", now)
1055                .await
1056                .unwrap(),
1057            vec![SocketAddr::new(ipv4_addr, 443)]
1058        );
1059
1060        update_cache(alice, "nine.testrun.org", "2a01:4f8:241:4ce8::2", now + 30)
1061            .await
1062            .unwrap();
1063
1064        // New DNS cache entry should go first.
1065        assert_eq!(
1066            lookup_cache(alice, "nine.testrun.org", 443, "", now + 60)
1067                .await
1068                .unwrap(),
1069            vec![
1070                SocketAddr::new(ipv6_addr, 443),
1071                SocketAddr::new(ipv4_addr, 443)
1072            ],
1073        );
1074
1075        // After successful connection to SMTP over port 465 using IPv4 address,
1076        // IPv4 address has higher priority.
1077        update_connection_history(
1078            alice,
1079            "smtp",
1080            "nine.testrun.org",
1081            465,
1082            "116.202.233.236",
1083            now + 100,
1084        )
1085        .await
1086        .unwrap();
1087        assert_eq!(
1088            lookup_cache(alice, "nine.testrun.org", 465, "smtp", now + 120)
1089                .await
1090                .unwrap(),
1091            vec![
1092                SocketAddr::new(ipv4_addr, 465),
1093                SocketAddr::new(ipv6_addr, 465)
1094            ]
1095        );
1096
1097        // For other ports and ALPNs order remains the same.
1098        assert_eq!(
1099            lookup_cache(alice, "nine.testrun.org", 993, "imap", now + 120)
1100                .await
1101                .unwrap(),
1102            vec![
1103                SocketAddr::new(ipv6_addr, 993),
1104                SocketAddr::new(ipv4_addr, 993)
1105            ],
1106        );
1107        assert_eq!(
1108            lookup_cache(alice, "nine.testrun.org", 465, "imap", now + 120)
1109                .await
1110                .unwrap(),
1111            vec![
1112                SocketAddr::new(ipv6_addr, 465),
1113                SocketAddr::new(ipv4_addr, 465)
1114            ],
1115        );
1116        assert_eq!(
1117            lookup_cache(alice, "nine.testrun.org", 993, "smtp", now + 120)
1118                .await
1119                .unwrap(),
1120            vec![
1121                SocketAddr::new(ipv6_addr, 993),
1122                SocketAddr::new(ipv4_addr, 993)
1123            ],
1124        );
1125    }
1126
1127    #[test]
1128    fn test_merge_with_cache() {
1129        let first_addr = IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1));
1130        let second_addr = IpAddr::V4(Ipv4Addr::new(192, 168, 1, 2));
1131
1132        // If there is no cache, just return resolved addresses.
1133        {
1134            let resolved_addrs = vec![
1135                SocketAddr::new(first_addr, 993),
1136                SocketAddr::new(second_addr, 993),
1137            ];
1138            let cache = vec![];
1139            assert_eq!(
1140                merge_with_cache(resolved_addrs.clone(), cache),
1141                resolved_addrs
1142            );
1143        }
1144
1145        // If cache contains address that is not in resolution results,
1146        // it is inserted in the merged result.
1147        {
1148            let resolved_addrs = vec![SocketAddr::new(first_addr, 993)];
1149            let cache = vec![SocketAddr::new(second_addr, 993)];
1150            assert_eq!(
1151                merge_with_cache(resolved_addrs, cache),
1152                vec![
1153                    SocketAddr::new(first_addr, 993),
1154                    SocketAddr::new(second_addr, 993),
1155                ]
1156            );
1157        }
1158
1159        // If cache contains address that is already in resolution results,
1160        // it is not duplicated.
1161        {
1162            let resolved_addrs = vec![
1163                SocketAddr::new(first_addr, 993),
1164                SocketAddr::new(second_addr, 993),
1165            ];
1166            let cache = vec![SocketAddr::new(second_addr, 993)];
1167            assert_eq!(
1168                merge_with_cache(resolved_addrs, cache),
1169                vec![
1170                    SocketAddr::new(first_addr, 993),
1171                    SocketAddr::new(second_addr, 993),
1172                ]
1173            );
1174        }
1175
1176        // If DNS resolvers returns a lot of results,
1177        // we should try cached results before going through all
1178        // the resolver results.
1179        {
1180            let resolved_addrs = vec![
1181                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 993),
1182                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), 993),
1183                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 3)), 993),
1184                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 4)), 993),
1185                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 5)), 993),
1186                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 6)), 993),
1187                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 7)), 993),
1188                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 8)), 993),
1189                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 9)), 993),
1190            ];
1191            let cache = vec![SocketAddr::new(second_addr, 993)];
1192            assert_eq!(
1193                merge_with_cache(resolved_addrs, cache),
1194                vec![
1195                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 993),
1196                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), 993),
1197                    SocketAddr::new(second_addr, 993),
1198                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 3)), 993),
1199                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 4)), 993),
1200                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 5)), 993),
1201                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 6)), 993),
1202                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 7)), 993),
1203                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 8)), 993),
1204                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 9)), 993),
1205                ]
1206            );
1207        }
1208
1209        // Even if cache already contains all the incorrect results
1210        // that resolver returns, this should not result in them being sorted to the top.
1211        // Cache has known to work result returned first,
1212        // so we should try it after the second result.
1213        {
1214            let resolved_addrs = vec![
1215                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 993),
1216                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), 993),
1217                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 3)), 993),
1218                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 4)), 993),
1219                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 5)), 993),
1220                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 6)), 993),
1221                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 7)), 993),
1222                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 8)), 993),
1223                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 9)), 993),
1224            ];
1225            let cache = vec![
1226                SocketAddr::new(second_addr, 993),
1227                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 9)), 993),
1228                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 8)), 993),
1229                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 7)), 993),
1230                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 6)), 993),
1231                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 5)), 993),
1232                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 4)), 993),
1233                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 3)), 993),
1234                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), 993),
1235            ];
1236            assert_eq!(
1237                merge_with_cache(resolved_addrs, cache),
1238                vec![
1239                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 993),
1240                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), 993),
1241                    SocketAddr::new(second_addr, 993),
1242                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 9)), 993),
1243                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 8)), 993),
1244                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 7)), 993),
1245                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 6)), 993),
1246                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 5)), 993),
1247                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 4)), 993),
1248                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 3)), 993),
1249                ]
1250            );
1251        }
1252    }
1253}