forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 0
/
mdns_responder.h
348 lines (305 loc) · 14.3 KB
/
mdns_responder.h
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SERVICES_NETWORK_MDNS_RESPONDER_H_
#define SERVICES_NETWORK_MDNS_RESPONDER_H_
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <string>
#include <vector>
#include "base/containers/flat_set.h"
#include "base/containers/unique_ptr_adapters.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/time/default_tick_clock.h"
#include "base/time/time.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "net/dns/dns_query.h"
#include "net/dns/dns_response.h"
#include "services/network/public/mojom/mdns_responder.mojom.h"
namespace net {
class IOBufferWithSize;
class IPAddress;
class MDnsSocketFactory;
} // namespace net
namespace network {
class MdnsResponder;
namespace mdns_helper {
// Creates an mDNS response, of which the Answer section contains the address
// records for |name_addr_map|, and the Additional section contains the
// corresponding NSEC records that assert the existence of only address
// records in the Answer section.
COMPONENT_EXPORT(NETWORK_SERVICE)
scoped_refptr<net::IOBufferWithSize> CreateResolutionResponse(
const base::TimeDelta& ttl,
const std::map<std::string, net::IPAddress>& name_addr_map);
// Creates an mDNS response, of which the Answer section contains NSEC records
// that assert the existence of only address records for |name_addr_map|, and
// the Additional section contains the corresponding address records.
COMPONENT_EXPORT(NETWORK_SERVICE)
scoped_refptr<net::IOBufferWithSize> CreateNegativeResponse(
const std::map<std::string, net::IPAddress>& name_addr_map);
// Creates an mDNS response to an mDNS name generator service query.
//
// An mDNS name generator service query is a query for TXT records associated
// with the name "Generated-Names._mdns_name_generator._udp.local". It is
// similar to the service query defined in DNS-SD but is not an implementation
// of the specification in RFC 6763. The Answer section of the response contains
// a TXT record for the list of |mdns_names|. The cache-flush bit is set in the
// TXT record if |cached_flush| is true.
COMPONENT_EXPORT(NETWORK_SERVICE)
scoped_refptr<net::IOBufferWithSize>
CreateResponseToMdnsNameGeneratorServiceQuery(
const base::TimeDelta& ttl,
const std::set<std::string>& mdns_names);
} // namespace mdns_helper
// Options to configure the transmission of mDNS responses.
struct COMPONENT_EXPORT(NETWORK_SERVICE) MdnsResponseSendOption
: public base::RefCounted<MdnsResponseSendOption> {
public:
enum class ResponseClass {
UNSPECIFIED,
ANNOUNCEMENT,
PROBE_RESOLUTION,
REGULAR_RESOLUTION,
NEGATIVE,
GOODBYE,
};
MdnsResponseSendOption();
// As a shorthand, an empty set denotes all interfaces.
std::set<uint16_t> send_socket_handler_ids;
// Used for rate limiting.
base::flat_set<std::string> names_for_rate_limit;
// Used for retry after send failure.
ResponseClass klass = ResponseClass::UNSPECIFIED;
// The number of retries done for the same response due to send failure.
uint8_t num_send_retries_done = 0;
// Indicates if the response includes a resource record that is a member of a
// shared resource record set.
bool shared_result = false;
// If not nullopt, returns true if the response to send is cancelled.
std::optional<base::RepeatingCallback<bool()>> cancelled_callback;
private:
friend class base::RefCounted<MdnsResponseSendOption>;
~MdnsResponseSendOption();
};
// The responder manager creates and manages responder instances spawned for
// each Mojo binding. It also manages the underlying network IO by delegating
// the IO task to socket handlers of each interface. When there is a network
// stack error or a Mojo binding error, the manager also offers the
// corresponding error handling.
class COMPONENT_EXPORT(NETWORK_SERVICE) MdnsResponderManager {
public:
// Wraps a name generation method that can be configured in tests via
// SetNameGeneratorForTesting below.
//
// The generated name by |CreateName| must be no more than 245 characters.
class NameGenerator {
public:
virtual ~NameGenerator() = default;
virtual std::string CreateName() = 0;
};
// Used in histograms to measure the service health.
enum class ServiceError {
// Fail to start the MdnsResponderManager after all socket handlers fail to
// start on each interface.
kFailToStartManager = 0,
// Fail to create a MdnsResponder after all socket handlers fail to start on
// each interface.
kFailToCreateResponder = 1,
// All socket handlers have encountered read errors and failed. Imminent to
// restart the MdnsResponderManager.
kFatalSocketHandlerError = 2,
// An invalid IP address is given to register an mDNS name for.
kInvalidIpToRegisterName = 3,
// A record is received from the network such that it resolves a name
// created
// by the service to a different address.
kConflictingNameResolution = 4,
kMaxValue = kConflictingNameResolution,
};
// Delay between throttled attempts to start the `MdnsResponderManager`.
constexpr static base::TimeDelta kManagerStartThrottleDelay =
base::Seconds(1);
MdnsResponderManager();
explicit MdnsResponderManager(net::MDnsSocketFactory* socket_factory);
MdnsResponderManager(const MdnsResponderManager&) = delete;
MdnsResponderManager& operator=(const MdnsResponderManager&) = delete;
~MdnsResponderManager();
// Creates an instance of MdnsResponder for the receiver.
void CreateMdnsResponder(
mojo::PendingReceiver<mojom::MdnsResponder> receiver);
// The methods below are used to bookkeep names owned by responders, and
// also for the extra uniqueness validation of these names. By default,
// we use the RandomUuidNameGenerator (see mdns_responder.cc), which
// probabilistically guarantees the uniqueness of generated names.
//
// Adds a name to the set of all existing names generated by all responders
// (i.e., names owned by an instance of responder). Return true if the name is
// not in the set before the addition; false otherwise.
bool AddName(const std::string& name) {
auto result = names_.insert(name);
return result.second;
}
// Removes a name from the set of all existing names generated by all
// responders. Return true if the name exists in the set before the removal;
// false otherwise.
bool RemoveName(const std::string& name) { return names_.erase(name) == 1; }
// Sends an mDNS response in the wire format given by |buf|. See
// MdnsResponseSendOption for configurable options in |option|.
//
// Sending responses is rate-limited, and this method returns true if the
// response is successfully scheduled to send on all successfully bound
// interfaces specified in |option.send_socket_handler_ids|, and false
// otherwise.
bool Send(scoped_refptr<net::IOBufferWithSize> buf,
scoped_refptr<MdnsResponseSendOption> option);
// The error handler that is invoked when the Mojo binding of |responder| is
// closed (e.g. the InterfacePtr on the client side is destroyed) or
// encounters an error. It removes this responder instance, which further
// clears the existing name-address associations owned by this responder in
// the local network.
void OnMojoConnectionError(MdnsResponder* responder);
// Called when an external mDNS response is received and it contains address
// records of names generated by an owned MdnsResponder instance.
void HandleAddressNameConflictIfAny(
const std::map<std::string, std::set<net::IPAddress>>&
external_address_maps);
// Called when an external mDNS response is received and it contains a TXT
// record for the mDNS name generator service instance name with the
// cache-flush bit set.
void HandleTxtNameConflict();
NameGenerator* name_generator() const { return name_generator_.get(); }
// Sets the name generator that is shared by all MdnsResponder instances.
// Changing the name generator affects all existing responder instances and
// also the ones spawned in the future.
//
// Used for tests only.
void SetNameGeneratorForTesting(
std::unique_ptr<NameGenerator> name_generator);
// Sets the tick clock that is used for rate limiting of mDNS responses, and
// also resets the internal schedule for rate limiting.
//
// Used for tests only.
void SetTickClockForTesting(const base::TickClock* tick_clock);
private:
enum class SocketHandlerStartResult {
UNSPECIFIED,
// Handlers started for all interfaces.
ALL_SUCCESS,
// Handlers started for a subset of interfaces.
PARTIAL_SUCCESS,
// No handler started.
ALL_FAILURE,
};
// Handles the underlying sending and receiving of mDNS messages on each
// interface available. The implementation mostly resembles
// net::MdnsConnection::SocketHandler.
class SocketHandler;
// Initializes socket handlers and sets `start_result_`. Safe to call multiple
// times after success or failure (and will noop after success). As a
// protection against spammy network usage this will also noop if called too
// soon after the last failure.
void StartIfNeeded();
// Dispatches a parsed query from a socket handler to each responder instance.
void OnMdnsQueryReceived(const net::DnsQuery& query,
uint16_t recv_socket_handler_id);
void OnSocketHandlerReadError(uint16_t socket_handler_id, int result);
bool IsFatalError(int result);
// Called when an mDNS name service query is received. The manager fills a
// list of registered names in a TXT record in the response.
void HandleMdnsNameGeneratorServiceQuery(const net::DnsQuery& query,
uint16_t recv_socket_handler_id);
// Sends a zero-TTL mDNS response with a TXT record of mDNS names from the
// last generator service response sent. No-op if no generator service
// response sent previously.
void SendGoodbyePacketForMdnsNameGeneratorServiceIfNecessary();
std::unique_ptr<net::MDnsSocketFactory> owned_socket_factory_;
raw_ptr<net::MDnsSocketFactory> socket_factory_;
// Only the socket handlers that have successfully bound and started are kept.
std::map<uint16_t, std::unique_ptr<SocketHandler>> socket_handler_by_id_;
SocketHandlerStartResult start_result_ =
SocketHandlerStartResult::UNSPECIFIED;
// Used to bookkeep all names owned by |responders_| for
// 1. the extra uniqueness validation of names generated by responders;
// 2. generating responses to the mDNS name generator service queries.
std::set<std::string> names_;
std::unique_ptr<NameGenerator> name_generator_;
// The names used to create the last response to a generator service query.
std::set<std::string> names_in_last_generator_response_;
bool should_respond_to_generator_service_query_ = true;
std::set<std::unique_ptr<MdnsResponder>, base::UniquePtrComparator>
responders_;
raw_ptr<const base::TickClock> tick_clock_ =
base::DefaultTickClock::GetInstance();
// If not `base::TimeTicks()`, represents the end of the throttling period for
// calls to `StartIfNeeded()`.
base::TimeTicks throttled_start_end_;
base::WeakPtrFactory<MdnsResponderManager> weak_factory_{this};
};
// Implementation of the mDNS service that can provide utilities of an mDNS
// responder.
class COMPONENT_EXPORT(NETWORK_SERVICE) MdnsResponder
: public mojom::MdnsResponder {
public:
MdnsResponder(mojo::PendingReceiver<mojom::MdnsResponder> receiver,
MdnsResponderManager* manager);
MdnsResponder(const MdnsResponder&) = delete;
MdnsResponder& operator=(const MdnsResponder&) = delete;
// When destroyed, clears all existing name-address associations owned by this
// responder in the local network by sending out goodbye packets. See
// SendGoodbyePacketForNameAddressMap below.
~MdnsResponder() override;
// mojom::MdnsResponder overrides.
void CreateNameForAddress(
const net::IPAddress& address,
mojom::MdnsResponder::CreateNameForAddressCallback callback) override;
void RemoveNameForAddress(
const net::IPAddress& address,
mojom::MdnsResponder::RemoveNameForAddressCallback callback) override;
// Processes the given query and generates a response if the query contains an
// mDNS name that this responder has a mapped IP address.
void OnMdnsQueryReceived(const net::DnsQuery& query,
uint16_t recv_socket_handler_id);
bool HasConflictWithExternalResolution(
const std::string& name,
const std::set<net::IPAddress>& external_mapped_addreses);
void SetNameGeneratorForTesting(
MdnsResponderManager::NameGenerator* name_generator) {
name_generator_ = name_generator;
}
private:
// Returns true if the response is successfully scheduled to send on all
// successfully bound interfaces after rate limiting, and false otherwise. See
// also MdnsResponderManager::Send.
bool SendMdnsResponse(scoped_refptr<net::IOBufferWithSize> response,
scoped_refptr<MdnsResponseSendOption> option);
// RFC 6761, Section 10.1 "Goodbye Packets".
//
// The responder should send out an unsolicited mDNS response with an resource
// record of zero TTL to clear the name-to-address mapping in neighboring
// hosts, when the mapping is no longer valid.
//
// Returns true if the goodbye message is successfully scheduled to send on
// all interfaces after rate limiting, and false otherwise. See also
// MdnsResponderManager::Send.
bool SendGoodbyePacketForNameAddressMap(
const std::map<std::string, net::IPAddress>& name_addr_map);
std::map<std::string, net::IPAddress>::iterator FindNameCreatedForAddress(
const net::IPAddress& address);
mojo::Receiver<network::mojom::MdnsResponder> receiver_;
// A back pointer to the responder manager that owns this responder. The
// responder should be destroyed before |manager_| becomes invalid or a weak
// reference should be used to access the manager when there is no such
// guarantee in an operation.
const raw_ptr<MdnsResponderManager> manager_;
std::map<std::string, net::IPAddress> name_addr_map_;
std::map<std::string, uint16_t> name_refcount_map_;
raw_ptr<MdnsResponderManager::NameGenerator> name_generator_;
};
} // namespace network
#endif // SERVICES_NETWORK_MDNS_RESPONDER_H_