1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef OSP_IMPL_DISCOVERY_MDNS_MDNS_RESPONDER_ADAPTER_H_
6 #define OSP_IMPL_DISCOVERY_MDNS_MDNS_RESPONDER_ADAPTER_H_
7 
8 #include <cstdint>
9 #include <map>
10 #include <string>
11 #include <vector>
12 
13 #include "osp/impl/discovery/mdns/domain_name.h"
14 #include "osp/impl/discovery/mdns/mdns_responder_platform.h"
15 #include "platform/api/network_interface.h"
16 #include "platform/api/time.h"
17 #include "platform/api/udp_socket.h"
18 #include "platform/base/error.h"
19 #include "platform/base/ip_address.h"
20 
21 namespace openscreen {
22 namespace osp {
23 
24 struct QueryEventHeader {
25   enum class Type {
26     kAdded = 0,
27     kAddedNoCache,
28     kRemoved,
29   };
30 
31   QueryEventHeader();
32   QueryEventHeader(Type response_type, UdpSocket* socket);
33   QueryEventHeader(QueryEventHeader&&) noexcept;
34   ~QueryEventHeader();
35   QueryEventHeader& operator=(QueryEventHeader&&) noexcept;
36 
37   Type response_type;
38   UdpSocket* socket;
39 };
40 
41 struct PtrEvent {
42   PtrEvent();
43   PtrEvent(QueryEventHeader header, DomainName service_instance);
44   PtrEvent(PtrEvent&&) noexcept;
45   ~PtrEvent();
46   PtrEvent& operator=(PtrEvent&&) noexcept;
47 
48   QueryEventHeader header;
49   DomainName service_instance;
50 };
51 
52 struct SrvEvent {
53   SrvEvent();
54   SrvEvent(QueryEventHeader header,
55            DomainName service_instance,
56            DomainName domain_name,
57            uint16_t port);
58   SrvEvent(SrvEvent&&) noexcept;
59   ~SrvEvent();
60   SrvEvent& operator=(SrvEvent&&) noexcept;
61 
62   QueryEventHeader header;
63   DomainName service_instance;
64   DomainName domain_name;
65   uint16_t port;
66 };
67 
68 struct TxtEvent {
69   TxtEvent();
70   TxtEvent(QueryEventHeader header,
71            DomainName service_instance,
72            std::vector<std::string> txt_info);
73   TxtEvent(TxtEvent&&) noexcept;
74   ~TxtEvent();
75   TxtEvent& operator=(TxtEvent&&) noexcept;
76 
77   QueryEventHeader header;
78   DomainName service_instance;
79 
80   // NOTE: mDNS does not specify a character encoding for the data in TXT
81   // records.
82   std::vector<std::string> txt_info;
83 };
84 
85 struct AEvent {
86   AEvent();
87   AEvent(QueryEventHeader header, DomainName domain_name, IPAddress address);
88   AEvent(AEvent&&) noexcept;
89   ~AEvent();
90   AEvent& operator=(AEvent&&) noexcept;
91 
92   QueryEventHeader header;
93   DomainName domain_name;
94   IPAddress address;
95 };
96 
97 struct AaaaEvent {
98   AaaaEvent();
99   AaaaEvent(QueryEventHeader header, DomainName domain_name, IPAddress address);
100   AaaaEvent(AaaaEvent&&) noexcept;
101   ~AaaaEvent();
102   AaaaEvent& operator=(AaaaEvent&&) noexcept;
103 
104   QueryEventHeader header;
105   DomainName domain_name;
106   IPAddress address;
107 };
108 
109 enum class MdnsResponderErrorCode {
110   kNoError = 0,
111   kUnsupportedError,
112   kDomainOverflowError,
113   kInvalidParameters,
114   kUnknownError,
115 };
116 
117 // This interface wraps all the functionality of mDNSResponder, which includes
118 // both listening and publishing.  As a result, some methods are only used by
119 // listeners, some are only used by publishers, and some are used by both.
120 //
121 // Listening for records might look like this:
122 //   adapter->Init();
123 //
124 //   // Once for each interface, the meaning of false is described below.
125 //   adapter->RegisterInterface(..., false);
126 //
127 //   adapter->StartPtrQuery("_openscreen._udp");
128 //   adapter->RunTasks();
129 //
130 //   // When receiving multicast UDP traffic from port 5353.
131 //   adapter->OnDataReceived(...);
132 //   adapter->RunTasks();
133 //
134 //   // Check |ptrs| for responses after pulling.
135 //   auto ptrs = adapter->TakePtrResponses();
136 //
137 //   // Eventually...
138 //   adapter->StopPtrQuery("_openscreen._udp");
139 //
140 // Publishing a service might look like this:
141 //   adapter->Init();
142 //
143 //   // Once for each interface, the meaning of true is described below.
144 //   adapter->RegisterInterface(..., true);
145 //
146 //   adapter->SetHostLabel("deadbeef");
147 //   adapter->RegisterService("living-room", "_openscreen._udp", ...);
148 //   adapter->RunTasks();
149 //
150 //   // When receiving multicast UDP traffic from port 5353.
151 //   adapter->OnDataReceived(...);
152 //   adapter->RunTasks();
153 //
154 //   // Eventually...
155 //   adapter->DeregisterService("living-room", "_openscreen", "_udp");
156 //
157 // Additionally, it's important to understand that mDNSResponder may defer some
158 // tasks (e.g. parsing responses, sending queries, etc.) and those deferred
159 // tasks are only run when RunTasks is called.  Therefore, RunTasks should be
160 // called after any sequence of calls to mDNSResponder.  It also returns a
161 // timeout value, after which it must be called again (e.g. for maintaining its
162 // cache).
163 class MdnsResponderAdapter : public UdpSocket::Client {
164  public:
165   MdnsResponderAdapter();
166   virtual ~MdnsResponderAdapter() = 0;
167 
168   // Initializes mDNSResponder.  This should be called before any queries or
169   // service registrations are made.
170   virtual Error Init() = 0;
171 
172   // Stops all open queries and service registrations.  If this is not called
173   // before destruction, any registered services will not send their goodbye
174   // messages.
175   virtual void Close() = 0;
176 
177   // Called to change the name published by the A and AAAA records for the host
178   // when any service is active (via RegisterService).  Returns true if the
179   // label was set successfully, false otherwise (e.g. the label did not meet
180   // DNS name requirements).
181   virtual Error SetHostLabel(const std::string& host_label) = 0;
182 
183   // The following methods register and deregister a network interface with
184   // mDNSResponder.  |socket| will be used to identify which interface received
185   // the data in OnDataReceived and will be used to send data via the platform
186   // layer.
187   virtual Error RegisterInterface(const InterfaceInfo& interface_info,
188                                   const IPSubnet& interface_address,
189                                   UdpSocket* socket) = 0;
190   virtual Error DeregisterInterface(UdpSocket* socket) = 0;
191 
192   // Returns the time period after which this method must be called again, if
193   // any.
194   virtual Clock::duration RunTasks() = 0;
195 
196   virtual std::vector<PtrEvent> TakePtrResponses() = 0;
197   virtual std::vector<SrvEvent> TakeSrvResponses() = 0;
198   virtual std::vector<TxtEvent> TakeTxtResponses() = 0;
199   virtual std::vector<AEvent> TakeAResponses() = 0;
200   virtual std::vector<AaaaEvent> TakeAaaaResponses() = 0;
201 
202   virtual MdnsResponderErrorCode StartPtrQuery(
203       UdpSocket* socket,
204       const DomainName& service_type) = 0;
205   virtual MdnsResponderErrorCode StartSrvQuery(
206       UdpSocket* socket,
207       const DomainName& service_instance) = 0;
208   virtual MdnsResponderErrorCode StartTxtQuery(
209       UdpSocket* socket,
210       const DomainName& service_instance) = 0;
211   virtual MdnsResponderErrorCode StartAQuery(UdpSocket* socket,
212                                              const DomainName& domain_name) = 0;
213   virtual MdnsResponderErrorCode StartAaaaQuery(
214       UdpSocket* socket,
215       const DomainName& domain_name) = 0;
216 
217   virtual MdnsResponderErrorCode StopPtrQuery(
218       UdpSocket* socket,
219       const DomainName& service_type) = 0;
220   virtual MdnsResponderErrorCode StopSrvQuery(
221       UdpSocket* socket,
222       const DomainName& service_instance) = 0;
223   virtual MdnsResponderErrorCode StopTxtQuery(
224       UdpSocket* socket,
225       const DomainName& service_instance) = 0;
226   virtual MdnsResponderErrorCode StopAQuery(UdpSocket* socket,
227                                             const DomainName& domain_name) = 0;
228   virtual MdnsResponderErrorCode StopAaaaQuery(
229       UdpSocket* socket,
230       const DomainName& domain_name) = 0;
231 
232   // The following methods concern advertising a service via mDNS.  The
233   // arguments correspond to values needed in the PTR, SRV, and TXT records that
234   // will be published for the service.  An A or AAAA record will also be
235   // published with the service for each active interface known to mDNSResponder
236   // via RegisterInterface.
237   virtual MdnsResponderErrorCode RegisterService(
238       const std::string& service_instance,
239       const std::string& service_name,
240       const std::string& service_protocol,
241       const DomainName& target_host,
242       uint16_t target_port,
243       const std::map<std::string, std::string>& txt_data) = 0;
244   virtual MdnsResponderErrorCode DeregisterService(
245       const std::string& service_instance,
246       const std::string& service_name,
247       const std::string& service_protocol) = 0;
248   virtual MdnsResponderErrorCode UpdateTxtData(
249       const std::string& service_instance,
250       const std::string& service_name,
251       const std::string& service_protocol,
252       const std::map<std::string, std::string>& txt_data) = 0;
253 };
254 
255 }  // namespace osp
256 }  // namespace openscreen
257 
258 #endif  // OSP_IMPL_DISCOVERY_MDNS_MDNS_RESPONDER_ADAPTER_H_
259