Skip to main content

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