1 /* vim:set ts=4 sw=2 sts=2 et cin: */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #ifndef nsHostRecord_h__
7 #define nsHostRecord_h__
8 
9 #include "mozilla/AtomicBitfields.h"
10 #include "mozilla/LinkedList.h"
11 #include "mozilla/net/HTTPSSVC.h"
12 #include "nsIDNSService.h"
13 #include "nsIDNSByTypeRecord.h"
14 #include "PLDHashTable.h"
15 #include "TRRSkippedReason.h"
16 
17 class nsHostRecord;
18 class nsHostResolver;
19 
20 namespace mozilla {
21 namespace net {
22 class HostRecordQueue;
23 class TRR;
24 class TRRQuery;
25 }  // namespace net
26 }  // namespace mozilla
27 
28 /**
29  * This class is used to notify listeners when a ResolveHost operation is
30  * complete. Classes that derive it must implement threadsafe nsISupports
31  * to be able to use RefPtr with this class.
32  */
33 class nsResolveHostCallback
34     : public mozilla::LinkedListElement<RefPtr<nsResolveHostCallback>>,
35       public nsISupports {
36  public:
37   /**
38    * OnResolveHostComplete
39    *
40    * this function is called to complete a host lookup initiated by
41    * nsHostResolver::ResolveHost.  it may be invoked recursively from
42    * ResolveHost or on an unspecified background thread.
43    *
44    * NOTE: it is the responsibility of the implementor of this method
45    * to handle the callback in a thread safe manner.
46    *
47    * @param resolver
48    *        nsHostResolver object associated with this result
49    * @param record
50    *        the host record containing the results of the lookup
51    * @param status
52    *        if successful, |record| contains non-null results
53    */
54   virtual void OnResolveHostComplete(nsHostResolver* resolver,
55                                      nsHostRecord* record, nsresult status) = 0;
56   /**
57    * EqualsAsyncListener
58    *
59    * Determines if the listener argument matches the listener member var.
60    * For subclasses not implementing a member listener, should return false.
61    * For subclasses having a member listener, the function should check if
62    * they are the same.  Used for cases where a pointer to an object
63    * implementing nsResolveHostCallback is unknown, but a pointer to
64    * the original listener is known.
65    *
66    * @param aListener
67    *        nsIDNSListener object associated with the original request
68    */
69   virtual bool EqualsAsyncListener(nsIDNSListener* aListener) = 0;
70 
71   virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf) const = 0;
72 
73  protected:
74   virtual ~nsResolveHostCallback() = default;
75 };
76 
77 struct nsHostKey {
78   const nsCString host;
79   const nsCString mTrrServer;
80   uint16_t type = 0;
81   uint16_t flags = 0;
82   uint16_t af = 0;
83   bool pb = false;
84   const nsCString originSuffix;
85   explicit nsHostKey(const nsACString& host, const nsACString& aTrrServer,
86                      uint16_t type, uint16_t flags, uint16_t af, bool pb,
87                      const nsACString& originSuffix);
88   bool operator==(const nsHostKey& other) const;
89   size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
90   PLDHashNumber Hash() const;
91 };
92 
93 /**
94  * nsHostRecord - ref counted object type stored in host resolver cache.
95  */
96 class nsHostRecord : public mozilla::LinkedListElement<RefPtr<nsHostRecord>>,
97                      public nsHostKey,
98                      public nsISupports {
99   using TRRSkippedReason = mozilla::net::TRRSkippedReason;
100 
101  public:
102   NS_DECL_THREADSAFE_ISUPPORTS
103 
SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)104   virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
105     return 0;
106   }
107 
108   // Returns the TRR mode encoded by the flags
109   nsIRequest::TRRMode TRRMode();
110 
111   // Records the first reason that caused TRR to be skipped or to fail.
RecordReason(TRRSkippedReason reason)112   void RecordReason(TRRSkippedReason reason) {
113     if (mTRRSkippedReason == TRRSkippedReason::TRR_UNSET) {
114       mTRRSkippedReason = reason;
115     }
116   }
117 
118   enum DnsPriority {
119     DNS_PRIORITY_LOW = nsIDNSService::RESOLVE_PRIORITY_LOW,
120     DNS_PRIORITY_MEDIUM = nsIDNSService::RESOLVE_PRIORITY_MEDIUM,
121     DNS_PRIORITY_HIGH,
122   };
123 
124  protected:
125   friend class nsHostResolver;
126   friend class mozilla::net::HostRecordQueue;
127   friend class mozilla::net::TRR;
128   friend class mozilla::net::TRRQuery;
129 
130   explicit nsHostRecord(const nsHostKey& key);
131   virtual ~nsHostRecord() = default;
132 
133   // Mark hostrecord as not usable
134   void Invalidate();
135 
136   enum ExpirationStatus {
137     EXP_VALID,
138     EXP_GRACE,
139     EXP_EXPIRED,
140   };
141 
142   ExpirationStatus CheckExpiration(const mozilla::TimeStamp& now) const;
143 
144   // Convenience function for setting the timestamps above (mValidStart,
145   // mValidEnd, and mGraceStart). valid and grace are durations in seconds.
146   void SetExpiration(const mozilla::TimeStamp& now, unsigned int valid,
147                      unsigned int grace);
148   void CopyExpirationTimesAndFlagsFrom(const nsHostRecord* aFromHostRecord);
149 
150   // Checks if the record is usable (not expired and has a value)
151   bool HasUsableResult(const mozilla::TimeStamp& now,
152                        uint16_t queryFlags = 0) const;
153 
154   static DnsPriority GetPriority(uint16_t aFlags);
155 
156   virtual void Cancel();
157   virtual bool HasUsableResultInternal(const mozilla::TimeStamp& now,
158                                        uint16_t queryFlags) const = 0;
RefreshForNegativeResponse()159   virtual bool RefreshForNegativeResponse() const { return true; }
160 
161   mozilla::LinkedList<RefPtr<nsResolveHostCallback>> mCallbacks;
162 
IsAddrRecord()163   bool IsAddrRecord() const {
164     return type == nsIDNSService::RESOLVE_TYPE_DEFAULT;
165   }
166 
167   // When the record began being valid. Used mainly for bookkeeping.
168   mozilla::TimeStamp mValidStart;
169 
170   // When the record is no longer valid (it's time of expiration)
171   mozilla::TimeStamp mValidEnd;
172 
173   // When the record enters its grace period. This must be before mValidEnd.
174   // If a record is in its grace period (and not expired), it will be used
175   // but a request to refresh it will be made.
176   mozilla::TimeStamp mGraceStart;
177 
178   uint32_t mTtl = 0;
179 
180   // The computed TRR mode that is actually used by the request.
181   // It is set in nsHostResolver::NameLookup and is based on the mode of the
182   // default resolver and the TRRMode encoded in the flags.
183   // The mode into account if the TRR service is disabled,
184   // parental controls are on, domain matches exclusion list, etc.
185   nsIRequest::TRRMode mEffectiveTRRMode = nsIRequest::TRR_DEFAULT_MODE;
186 
187   TRRSkippedReason mTRRSkippedReason = TRRSkippedReason::TRR_UNSET;
188   TRRSkippedReason mTRRAFailReason = TRRSkippedReason::TRR_UNSET;
189   TRRSkippedReason mTRRAAAAFailReason = TRRSkippedReason::TRR_UNSET;
190 
191   mozilla::DataMutex<RefPtr<mozilla::net::TRRQuery>> mTRRQuery;
192 
193   // counter of outstanding resolving calls
194   mozilla::Atomic<int32_t> mResolving{0};
195 
196   // True if this record is a cache of a failed lookup.  Negative cache
197   // entries are valid just like any other (though never for more than 60
198   // seconds), but a use of that negative entry forces an asynchronous refresh.
199   bool negative = false;
200 
201   // Explicitly expired
202   bool mDoomed = false;
203 };
204 
205 // b020e996-f6ab-45e5-9bf5-1da71dd0053a
206 #define ADDRHOSTRECORD_IID                           \
207   {                                                  \
208     0xb020e996, 0xf6ab, 0x45e5, {                    \
209       0x9b, 0xf5, 0x1d, 0xa7, 0x1d, 0xd0, 0x05, 0x3a \
210     }                                                \
211   }
212 
213 class AddrHostRecord final : public nsHostRecord {
214   using Mutex = mozilla::Mutex;
215   using DNSResolverType = mozilla::net::DNSResolverType;
216 
217  public:
NS_DECLARE_STATIC_IID_ACCESSOR(ADDRHOSTRECORD_IID)218   NS_DECLARE_STATIC_IID_ACCESSOR(ADDRHOSTRECORD_IID)
219   NS_DECL_ISUPPORTS_INHERITED
220 
221   /* a fully resolved host record has either a non-null |addr_info| or |addr|
222    * field.  if |addr_info| is null, it implies that the |host| is an IP
223    * address literal.  in which case, |addr| contains the parsed address.
224    * otherwise, if |addr_info| is non-null, then it contains one or many
225    * IP addresses corresponding to the given host name.  if both |addr_info|
226    * and |addr| are null, then the given host has not yet been fully resolved.
227    * |af| is the address family of the record we are querying for.
228    */
229 
230   /* the lock protects |addr_info| and |addr_info_gencnt| because they
231    * are mutable and accessed by the resolver worker thread and the
232    * nsDNSService2 class.  |addr| doesn't change after it has been
233    * assigned a value.  only the resolver worker thread modifies
234    * nsHostRecord (and only in nsHostResolver::CompleteLookup);
235    * the other threads just read it.  therefore the resolver worker
236    * thread doesn't need to lock when reading |addr_info|.
237    */
238   Mutex addr_info_lock{"AddrHostRecord.addr_info_lock"};
239   // generation count of |addr_info|
240   int addr_info_gencnt = 0;
241   RefPtr<mozilla::net::AddrInfo> addr_info;
242   mozilla::UniquePtr<mozilla::net::NetAddr> addr;
243 
244   // hold addr_info_lock when calling the blocklist functions
245   bool Blocklisted(const mozilla::net::NetAddr* query);
246   void ResetBlocklist();
247   void ReportUnusable(const mozilla::net::NetAddr* aAddress);
248 
249   size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const override;
250 
EffectiveTRRMode()251   nsIRequest::TRRMode EffectiveTRRMode() const { return mEffectiveTRRMode; }
252 
253  private:
254   friend class nsHostResolver;
255   friend class mozilla::net::HostRecordQueue;
256   friend class mozilla::net::TRR;
257   friend class mozilla::net::TRRQuery;
258 
259   explicit AddrHostRecord(const nsHostKey& key);
260   ~AddrHostRecord();
261 
262   // Checks if the record is usable (not expired and has a value)
263   bool HasUsableResultInternal(const mozilla::TimeStamp& now,
264                                uint16_t queryFlags) const override;
265 
266   bool RemoveOrRefresh(bool aTrrToo);  // Mark records currently being resolved
267                                        // as needed to resolve again.
268 
269   void ResolveComplete();
270 
271   static DnsPriority GetPriority(uint16_t aFlags);
272 
273   // true if pending and on the queue (not yet given to getaddrinfo())
onQueue()274   bool onQueue() { return LoadNative() && isInList(); }
275 
276   // When the lookups of this record started and their durations
277   mozilla::TimeStamp mTrrStart;
278   mozilla::TimeStamp mNativeStart;
279   mozilla::TimeDuration mTrrDuration;
280   mozilla::TimeDuration mNativeDuration;
281 
282   // TRR or ODoH was used on this record
283   mozilla::Atomic<DNSResolverType> mResolverType{DNSResolverType::Native};
284   uint8_t mTRRSuccess = 0;     // number of successful TRR responses
285   uint8_t mNativeSuccess = 0;  // number of native lookup responses
286 
287   // clang-format off
288   MOZ_ATOMIC_BITFIELDS(mAtomicBitfields, 8, (
289     // true if this record is being resolved "natively", which means that
290     // it is either on the pending queue or owned by one of the worker threads.
291     (uint16_t, Native, 1),
292     (uint16_t, NativeUsed, 1),
293     // true if off queue and contributing to mActiveAnyThreadCount
294     (uint16_t, UsingAnyThread, 1),
295     (uint16_t, GetTtl, 1),
296     (uint16_t, ResolveAgain, 1)
297   ))
298   // clang-format on
299 
300   // The number of times ReportUnusable() has been called in the record's
301   // lifetime.
302   uint32_t mUnusableCount = 0;
303 
304   // a list of addresses associated with this record that have been reported
305   // as unusable. the list is kept as a set of strings to make it independent
306   // of gencnt.
307   nsTArray<nsCString> mUnusableItems;
308 };
309 
NS_DEFINE_STATIC_IID_ACCESSOR(AddrHostRecord,ADDRHOSTRECORD_IID)310 NS_DEFINE_STATIC_IID_ACCESSOR(AddrHostRecord, ADDRHOSTRECORD_IID)
311 
312 // 77b786a7-04be-44f2-987c-ab8aa96676e0
313 #define TYPEHOSTRECORD_IID                           \
314   {                                                  \
315     0x77b786a7, 0x04be, 0x44f2, {                    \
316       0x98, 0x7c, 0xab, 0x8a, 0xa9, 0x66, 0x76, 0xe0 \
317     }                                                \
318   }
319 
320 class TypeHostRecord final : public nsHostRecord,
321                              public nsIDNSTXTRecord,
322                              public nsIDNSHTTPSSVCRecord,
323                              public mozilla::net::DNSHTTPSSVCRecordBase {
324  public:
325   NS_DECLARE_STATIC_IID_ACCESSOR(TYPEHOSTRECORD_IID)
326   NS_DECL_ISUPPORTS_INHERITED
327   NS_DECL_NSIDNSTXTRECORD
328   NS_DECL_NSIDNSHTTPSSVCRECORD
329 
330   size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const override;
331   uint32_t GetType();
332   mozilla::net::TypeRecordResultType GetResults();
333 
334  private:
335   friend class nsHostResolver;
336   friend class mozilla::net::TRRQuery;
337 
338   explicit TypeHostRecord(const nsHostKey& key);
339   ~TypeHostRecord();
340 
341   // Checks if the record is usable (not expired and has a value)
342   bool HasUsableResultInternal(const mozilla::TimeStamp& now,
343                                uint16_t queryFlags) const override;
344   bool RefreshForNegativeResponse() const override;
345 
346   mozilla::net::TypeRecordResultType mResults = AsVariant(mozilla::Nothing());
347   mozilla::Mutex mResultsLock{"TypeHostRecord.mResultsLock"};
348 
349   // When the lookups of this record started (for telemetry).
350   mozilla::TimeStamp mStart;
351   bool mAllRecordsExcluded = false;
352 };
353 
NS_DEFINE_STATIC_IID_ACCESSOR(TypeHostRecord,TYPEHOSTRECORD_IID)354 NS_DEFINE_STATIC_IID_ACCESSOR(TypeHostRecord, TYPEHOSTRECORD_IID)
355 
356 static inline bool IsHighPriority(uint16_t flags) {
357   return !(flags & (nsHostRecord::DNS_PRIORITY_LOW |
358                     nsHostRecord::DNS_PRIORITY_MEDIUM));
359 }
360 
IsMediumPriority(uint16_t flags)361 static inline bool IsMediumPriority(uint16_t flags) {
362   return flags & nsHostRecord::DNS_PRIORITY_MEDIUM;
363 }
364 
IsLowPriority(uint16_t flags)365 static inline bool IsLowPriority(uint16_t flags) {
366   return flags & nsHostRecord::DNS_PRIORITY_LOW;
367 }
368 
369 #endif  // nsHostRecord_h__
370