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