libpromeki 1.0.0-alpha
PROfessional MEdia toolKIt
 
Loading...
Searching...
No Matches
mdnsbrowser.h
Go to the documentation of this file.
1
8#pragma once
9
10
11#include <promeki/config.h>
12#if PROMEKI_ENABLE_MDNS
13#include <promeki/namespace.h>
14#include <promeki/atomic.h>
15#include <promeki/buffer.h>
16#include <promeki/duration.h>
17#include <promeki/error.h>
18#include <promeki/list.h>
19#include <promeki/map.h>
20#include <promeki/mdnsrecord.h>
23#include <promeki/mutex.h>
25#include <promeki/objectbase.h>
27#include <promeki/string.h>
28#include <promeki/timestamp.h>
29
30PROMEKI_NAMESPACE_BEGIN
31
32class DnsRecord;
33class MdnsManager;
34using MdnsParsedRecord = DnsRecord;
35
90class MdnsBrowser : public ObjectBase {
91 PROMEKI_OBJECT(MdnsBrowser, ObjectBase)
92 public:
102 explicit MdnsBrowser(const MdnsServiceType &type, ObjectBase *parent = nullptr);
103
105 ~MdnsBrowser() override;
106
117 void setManager(MdnsManager *manager);
118
120 MdnsManager *manager() const { return _manager; }
121
132 MdnsManager *effectiveManager() const;
133
135 const MdnsServiceType &serviceType() const { return _type; }
136
148 bool isActive() const { return _active.value(); }
149
165 Error start();
166
174 void stop();
175
183 Error sendQuery();
184
200 void onManagerTick(const TimeStamp &now);
201
203 static constexpr int64_t InitialQueryIntervalMs = 1000;
204
206 static constexpr int64_t MaxQueryIntervalMs = 3600 * 1000;
207
218 static constexpr int64_t CacheFlushGraceMs = 1000;
219
234 static constexpr int64_t DirectedQueryDebounceMs = 2000;
235
245 Duration currentBackoffInterval() const;
246
256 uint64_t queryFireCount() const { return _queryFireCount.value(); }
257
267 int evictExpired();
268
276 int evictExpiredAt(const TimeStamp &now);
277
286 List<MdnsServiceInstance> instances() const;
287
297 void handlePacket(const Buffer &data, const SocketAddress &sender,
298 const NetworkInterface &iface);
299
301 void clearCache();
302
304 PROMEKI_SIGNAL(serviceFound, MdnsServiceInstance);
306 PROMEKI_SIGNAL(serviceUpdated, MdnsServiceInstance);
308 PROMEKI_SIGNAL(serviceLost, MdnsServiceInstance);
309
310 private:
311 // Internal per-instance bookkeeping. Stored in a Map
312 // keyed by the lower-cased instance FQDN so DNS's
313 // case-insensitive match is honoured cheaply.
314 struct Entry {
315 MdnsServiceInstance instance;
316 TimeStamp lastSeen;
317 Duration ttl;
318 // Per-family cache-flush timestamps used to
319 // implement the RFC 6762 §10.2 "1-second
320 // grace" — a cache-flush A/AAAA arriving
321 // within @ref CacheFlushGraceMs of the prior
322 // one is treated as a continuation of the
323 // same multi-record announce and appended
324 // rather than wiping the list.
325 TimeStamp lastV4FlushAt;
326 TimeStamp lastV6FlushAt;
327 // Per-record-type "last directed query sent"
328 // timestamps for the follow-up query
329 // debounce (see @ref DirectedQueryDebounceMs).
330 // Index by @ref MdnsParsedRecord::Type cast
331 // to size_t via @ref directedQuerySlot.
332 TimeStamp lastDirectedQuerySrvAt;
333 TimeStamp lastDirectedQueryTxtAt;
334 TimeStamp lastDirectedQueryAAt;
335 TimeStamp lastDirectedQueryAaaaAt;
336 bool foundEmitted = false;
337 bool hasPtr = false;
338 };
339
340 struct DirectedQuery {
341 String name;
342 uint16_t recordType = 0;
343 };
344
345 // Helper: extract the instance label from a full
346 // <Instance>._<app>._<proto>.<domain>. FQDN given the
347 // browser's service-type FQDN. Returns an empty
348 // String when @p targetFqdn does not end with the
349 // expected suffix.
350 String extractInstanceLabel(const String &targetFqdn) const;
351
352 // Returns a case-folded FQDN suitable for map keying.
353 static String foldName(const String &s);
354
355 // Applies one record to the cache. May emit
356 // serviceFound / serviceUpdated / serviceLost — the
357 // signals are emitted with the mutex released.
358 // Any directed follow-up queries needed to fill in
359 // gaps (e.g. PTR → SRV/TXT, SRV → A/AAAA) are
360 // appended to @p directedQueries; the caller is
361 // responsible for issuing them after the mutex is
362 // released.
363 void processRecord(const MdnsParsedRecord &record,
364 const NetworkInterface &iface,
365 List<DirectedQuery> &directedQueries);
366
367 // Helper used by @ref processRecord. Examines the
368 // given @p entry against the "what we still need"
369 // gap list and stages directed queries onto
370 // @p directedQueries when the @ref DirectedQueryDebounceMs
371 // gate allows. Updates the entry's debounce
372 // timestamps in place when a query is staged.
373 void planDirectedFollowups(Entry &entry, const TimeStamp &now,
374 List<DirectedQuery> &directedQueries);
375
376 // Issues every staged directed query off-lock. No-op
377 // when @ref _manager is null or not active.
378 void issueDirectedQueries(const List<DirectedQuery> &queries);
379
380 // Builds the Known-Answer Suppression list for an
381 // outbound continuous-query — one PTR record per
382 // currently-cached, still-fresh entry. Walks under
383 // @ref _entriesMtx; safe to call from any thread.
384 List<MdnsRecord> composeKnownAnswers() const;
385
386 // Returns @c true when the instance has the minimal
387 // identity needed to be exposed via @ref serviceFound
388 // (port + hostname). Address records may arrive
389 // later; tests can decide separately how strict to be.
390 static bool isAddressable(const MdnsServiceInstance &inst);
391
392 MdnsServiceType _type;
393 MdnsManager *_manager = nullptr;
394 mutable Mutex _entriesMtx;
395 Map<String, Entry> _entries;
396 Atomic<bool> _active;
397 Atomic<uint64_t> _queryFireCount;
398
399 // Backoff schedule — protected by the same mutex as
400 // the entries map. Reset to the initial interval on
401 // every @ref start; doubled by @ref onManagerTick.
402 TimeStamp _nextQueryAt;
403 Duration _currentInterval;
404};
405
406PROMEKI_NAMESPACE_END
407
408#endif // PROMEKI_ENABLE_MDNS