1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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 #include "nsDNSServiceDiscovery.h"
7 #include "MDNSResponderOperator.h"
8 #include "nsICancelable.h"
9 #include "nsXULAppAPI.h"
10 #include "private/pprio.h"
11
12 namespace mozilla {
13 namespace net {
14
15 namespace {
16
StartService()17 inline void StartService() {}
18
StopService()19 inline void StopService() {}
20
21 class ServiceCounter {
22 public:
IsServiceRunning()23 static bool IsServiceRunning() { return !!sUseCount; }
24
25 private:
26 static uint32_t sUseCount;
27
28 protected:
ServiceCounter()29 ServiceCounter() {
30 MOZ_ASSERT(NS_IsMainThread());
31 if (!sUseCount++) {
32 StartService();
33 }
34 }
35
~ServiceCounter()36 virtual ~ServiceCounter() {
37 MOZ_ASSERT(NS_IsMainThread());
38 if (!--sUseCount) {
39 StopService();
40 }
41 }
42 };
43
44 uint32_t ServiceCounter::sUseCount = 0;
45
46 class DiscoveryRequest final : public nsICancelable, private ServiceCounter {
47 public:
48 NS_DECL_ISUPPORTS
49 NS_DECL_NSICANCELABLE
50
51 explicit DiscoveryRequest(nsDNSServiceDiscovery* aService,
52 nsIDNSServiceDiscoveryListener* aListener);
53
54 private:
~DiscoveryRequest()55 virtual ~DiscoveryRequest() { Cancel(NS_OK); }
56
57 RefPtr<nsDNSServiceDiscovery> mService;
58 nsIDNSServiceDiscoveryListener* mListener;
59 };
60
NS_IMPL_ISUPPORTS(DiscoveryRequest,nsICancelable)61 NS_IMPL_ISUPPORTS(DiscoveryRequest, nsICancelable)
62
63 DiscoveryRequest::DiscoveryRequest(nsDNSServiceDiscovery* aService,
64 nsIDNSServiceDiscoveryListener* aListener)
65 : mService(aService), mListener(aListener) {}
66
67 NS_IMETHODIMP
Cancel(nsresult aReason)68 DiscoveryRequest::Cancel(nsresult aReason) {
69 if (mService) {
70 mService->StopDiscovery(mListener);
71 }
72
73 mService = nullptr;
74 return NS_OK;
75 }
76
77 class RegisterRequest final : public nsICancelable, private ServiceCounter {
78 public:
79 NS_DECL_ISUPPORTS
80 NS_DECL_NSICANCELABLE
81
82 explicit RegisterRequest(nsDNSServiceDiscovery* aService,
83 nsIDNSRegistrationListener* aListener);
84
85 private:
~RegisterRequest()86 virtual ~RegisterRequest() { Cancel(NS_OK); }
87
88 RefPtr<nsDNSServiceDiscovery> mService;
89 nsIDNSRegistrationListener* mListener;
90 };
91
NS_IMPL_ISUPPORTS(RegisterRequest,nsICancelable)92 NS_IMPL_ISUPPORTS(RegisterRequest, nsICancelable)
93
94 RegisterRequest::RegisterRequest(nsDNSServiceDiscovery* aService,
95 nsIDNSRegistrationListener* aListener)
96 : mService(aService), mListener(aListener) {}
97
98 NS_IMETHODIMP
Cancel(nsresult aReason)99 RegisterRequest::Cancel(nsresult aReason) {
100 if (mService) {
101 mService->UnregisterService(mListener);
102 }
103
104 mService = nullptr;
105 return NS_OK;
106 }
107
108 } // namespace
109
NS_IMPL_ISUPPORTS(nsDNSServiceDiscovery,nsIDNSServiceDiscovery)110 NS_IMPL_ISUPPORTS(nsDNSServiceDiscovery, nsIDNSServiceDiscovery)
111
112 nsresult nsDNSServiceDiscovery::Init() {
113 if (!XRE_IsParentProcess()) {
114 MOZ_ASSERT(false,
115 "nsDNSServiceDiscovery can only be used in parent process");
116 return NS_ERROR_FAILURE;
117 }
118 return NS_OK;
119 }
120
121 NS_IMETHODIMP
StartDiscovery(const nsACString & aServiceType,nsIDNSServiceDiscoveryListener * aListener,nsICancelable ** aRetVal)122 nsDNSServiceDiscovery::StartDiscovery(const nsACString& aServiceType,
123 nsIDNSServiceDiscoveryListener* aListener,
124 nsICancelable** aRetVal) {
125 MOZ_ASSERT(aRetVal);
126
127 nsresult rv;
128 if (NS_WARN_IF(NS_FAILED(rv = StopDiscovery(aListener)))) {
129 return rv;
130 }
131
132 nsCOMPtr<nsICancelable> req = new DiscoveryRequest(this, aListener);
133 RefPtr<BrowseOperator> browserOp =
134 new BrowseOperator(aServiceType, aListener);
135 if (NS_WARN_IF(NS_FAILED(rv = browserOp->Start()))) {
136 return rv;
137 }
138
139 mDiscoveryMap.InsertOrUpdate(aListener, std::move(browserOp));
140
141 req.forget(aRetVal);
142
143 return NS_OK;
144 }
145
146 NS_IMETHODIMP
StopDiscovery(nsIDNSServiceDiscoveryListener * aListener)147 nsDNSServiceDiscovery::StopDiscovery(
148 nsIDNSServiceDiscoveryListener* aListener) {
149 nsresult rv;
150
151 RefPtr<BrowseOperator> browserOp;
152 if (!mDiscoveryMap.Get(aListener, getter_AddRefs(browserOp))) {
153 return NS_OK;
154 }
155
156 browserOp->Cancel(); // cancel non-started operation
157 if (NS_WARN_IF(NS_FAILED(rv = browserOp->Stop()))) {
158 return rv;
159 }
160
161 mDiscoveryMap.Remove(aListener);
162 return NS_OK;
163 }
164
165 NS_IMETHODIMP
RegisterService(nsIDNSServiceInfo * aServiceInfo,nsIDNSRegistrationListener * aListener,nsICancelable ** aRetVal)166 nsDNSServiceDiscovery::RegisterService(nsIDNSServiceInfo* aServiceInfo,
167 nsIDNSRegistrationListener* aListener,
168 nsICancelable** aRetVal) {
169 MOZ_ASSERT(aRetVal);
170
171 nsresult rv;
172 if (NS_WARN_IF(NS_FAILED(rv = UnregisterService(aListener)))) {
173 return rv;
174 }
175
176 nsCOMPtr<nsICancelable> req = new RegisterRequest(this, aListener);
177 RefPtr<RegisterOperator> registerOp =
178 new RegisterOperator(aServiceInfo, aListener);
179 if (NS_WARN_IF(NS_FAILED(rv = registerOp->Start()))) {
180 return rv;
181 }
182
183 mRegisterMap.InsertOrUpdate(aListener, std::move(registerOp));
184
185 req.forget(aRetVal);
186
187 return NS_OK;
188 }
189
190 NS_IMETHODIMP
UnregisterService(nsIDNSRegistrationListener * aListener)191 nsDNSServiceDiscovery::UnregisterService(
192 nsIDNSRegistrationListener* aListener) {
193 nsresult rv;
194
195 RefPtr<RegisterOperator> registerOp;
196 if (!mRegisterMap.Get(aListener, getter_AddRefs(registerOp))) {
197 return NS_OK;
198 }
199
200 registerOp->Cancel(); // cancel non-started operation
201 if (NS_WARN_IF(NS_FAILED(rv = registerOp->Stop()))) {
202 return rv;
203 }
204
205 mRegisterMap.Remove(aListener);
206 return NS_OK;
207 }
208
209 NS_IMETHODIMP
ResolveService(nsIDNSServiceInfo * aServiceInfo,nsIDNSServiceResolveListener * aListener)210 nsDNSServiceDiscovery::ResolveService(nsIDNSServiceInfo* aServiceInfo,
211 nsIDNSServiceResolveListener* aListener) {
212 if (!ServiceCounter::IsServiceRunning()) {
213 return NS_ERROR_FAILURE;
214 }
215
216 nsresult rv;
217
218 RefPtr<ResolveOperator> resolveOp =
219 new ResolveOperator(aServiceInfo, aListener);
220 if (NS_WARN_IF(NS_FAILED(rv = resolveOp->Start()))) {
221 return rv;
222 }
223
224 return NS_OK;
225 }
226
227 } // namespace net
228 } // namespace mozilla
229