aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/actors/dnscachelib/dnscache.h
blob: 6a7061e2fe5378c7dc9d46c1ccd4da5ec7be97fc (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#pragma once

#include <contrib/libs/c-ares/include/ares.h>
#include <util/generic/map.h>
#include <util/generic/vector.h>
#include <util/network/address.h>
#include <util/system/mutex.h>
#include <util/datetime/base.h>

/** Asynchronous DNS resolver.
 *
 * This is NOT general purpose resolver! It is designed with very specific assumptions:
 * 1) there is relatively small and rarely changed set of resolved names (like, server pool in cluster)
 * 2) this names supposed to have addresses, absense of A record is equal to DNS error
 * 3) most of the time IP addresses do not change
 * 4) it's OK to return old IP address when DNS server not responding in time
 */

class TDnsCache {
public:
    TDnsCache(bool allowIpv4 = true, bool allowIpv6 = true, time_t entry_lifetime = 1800, time_t neg_lifetime = 1, ui32 request_timeout = 500000);
    ~TDnsCache();

    TString GetHostByAddr(const NAddr::IRemoteAddr&);

    // ip in network byte order
    TIpHost Get(const TString& host);

    /* use with AF_INET, AF_INET6 or AF_UNSPEC */
    NAddr::IRemoteAddrPtr GetAddr(const TString& host,
                                  int family,
                                  TIpPort port = 0,
                                  bool cacheOnly = false);

    void GetAllAddresses(const TString& host, TVector<NAddr::IRemoteAddrPtr>&);

    void GetStats(ui64& a_cache_hits, ui64& a_cache_misses,
                  ui64& ptr_cache_hits, ui64& ptr_cache_misses);

protected:
    bool ValidateHName(const TString& host) const noexcept;

private:
    struct TGHBNContext {
        TDnsCache* Owner;
        TString Hostname;
        int Family;
    };

    struct TGHBAContext {
        TDnsCache* Owner;
        in6_addr Addr;
    };

    struct THost {
        THost() noexcept {
        }

        TVector<TIpHost> AddrsV4;
        time_t ResolvedV4 = 0;
        time_t NotFoundV4 = 0;
        TAtomic InProgressV4 = 0;

        TVector<in6_addr> AddrsV6;
        time_t ResolvedV6 = 0;
        time_t NotFoundV6 = 0;
        TAtomic InProgressV6 = 0;

        TString AddrsV4ToString() const;
        TString AddrsV6ToString() const;

        bool IsStale(int family, const TDnsCache* ctx) const noexcept;
    };

    typedef TMap<TString, THost> THostCache;

    struct TAddr {
        TString Hostname;
        time_t Resolved = 0;
        time_t NotFound = 0;
        TAtomic InProgress = 0;
    };
    /* IRemoteAddr is annoingly hard to use, so I'll use in6_addr as key
     * and put v4 addrs in it.
     */
    struct TAddrCmp {
        bool operator()(const in6_addr& left, const in6_addr& right) const {
            for (size_t i = 0; i < sizeof(left); i++) {
                if (left.s6_addr[i] < right.s6_addr[i]) {
                    return true;
                } else if (left.s6_addr[i] > right.s6_addr[i]) {
                    return false;
                }
            }
            // equal
            return false;
        }
    };
    typedef TMap<in6_addr, TAddr, TAddrCmp> TAddrCache;

    const THost& Resolve(const TString&, int family, bool cacheOnly = false);

    const TAddr& ResolveAddr(const in6_addr&, int family);

    void WaitTask(TAtomic&);

    static void GHBNCallback(void* arg, int status, int timeouts,
                             struct hostent* info);

    static void GHBACallback(void* arg, int status, int timeouts,
                             struct hostent* info);

    const time_t EntryLifetime;
    const time_t NegativeLifetime;
    const TDuration Timeout;
    const bool AllowIpV4;
    const bool AllowIpV6;

    TMutex CacheMtx;
    THostCache HostCache;
    TAddrCache AddrCache;
    ui64 ACacheHits;
    ui64 ACacheMisses;
    ui64 PtrCacheHits;
    ui64 PtrCacheMisses;

    const static THost NullHost;

    TMutex AresMtx;
    void* Channel;

    struct TAresLibInit {
        TAresLibInit() {
#ifdef _win_
            const auto res = ares_library_init(ARES_LIB_INIT_ALL);
            Y_VERIFY(res == 0);
#endif
        }

        ~TAresLibInit() {
#ifdef _win_
            ares_library_cleanup();
#endif
        }
    };

    static TAresLibInit InitAresLib;
};