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 "MDNSResponderOperator.h"
7 #include "MDNSResponderReply.h"
8 #include "mozilla/EndianUtils.h"
9 #include "mozilla/Logging.h"
10 #include "mozilla/ScopeExit.h"
11 #include "mozilla/Unused.h"
12 #include "nsComponentManagerUtils.h"
13 #include "nsCOMPtr.h"
14 #include "nsDebug.h"
15 #include "nsDNSServiceInfo.h"
16 #include "nsHashPropertyBag.h"
17 #include "nsIProperty.h"
18 #include "nsISimpleEnumerator.h"
19 #include "nsIVariant.h"
20 #include "nsServiceManagerUtils.h"
21 #include "nsNetAddr.h"
22 #include "nsNetCID.h"
23 #include "nsSocketTransportService2.h"
24 #include "nsThreadUtils.h"
25 #include "nsXPCOMCID.h"
26 #include "private/pprio.h"
27 
28 #include "nsASocketHandler.h"
29 
30 namespace mozilla {
31 namespace net {
32 
33 static LazyLogModule gMDNSLog("MDNSResponderOperator");
34 #undef LOG_I
35 #define LOG_I(...) \
36   MOZ_LOG(mozilla::net::gMDNSLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
37 #undef LOG_E
38 #define LOG_E(...) \
39   MOZ_LOG(mozilla::net::gMDNSLog, mozilla::LogLevel::Error, (__VA_ARGS__))
40 
41 class MDNSResponderOperator::ServiceWatcher final : public nsASocketHandler {
42  public:
43   NS_DECL_THREADSAFE_ISUPPORTS
44 
45   // nsASocketHandler methods
OnSocketReady(PRFileDesc * fd,int16_t outFlags)46   virtual void OnSocketReady(PRFileDesc* fd, int16_t outFlags) override {
47     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
48     MOZ_ASSERT(fd == mFD);
49 
50     if (outFlags & (PR_POLL_ERR | PR_POLL_HUP | PR_POLL_NVAL)) {
51       LOG_E("error polling on listening socket (%p)", fd);
52       mCondition = NS_ERROR_UNEXPECTED;
53     }
54 
55     if (!(outFlags & PR_POLL_READ)) {
56       return;
57     }
58 
59     DNSServiceProcessResult(mService);
60   }
61 
OnSocketDetached(PRFileDesc * fd)62   virtual void OnSocketDetached(PRFileDesc* fd) override {
63     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
64     MOZ_ASSERT(mThread);
65     MOZ_ASSERT(fd == mFD);
66 
67     if (!mFD) {
68       return;
69     }
70 
71     // Bug 1175387: do not double close the handle here.
72     PR_ChangeFileDescNativeHandle(mFD, -1);
73     PR_Close(mFD);
74     mFD = nullptr;
75 
76     mThread->Dispatch(
77         NewRunnableMethod("MDNSResponderOperator::ServiceWatcher::Deallocate",
78                           this, &ServiceWatcher::Deallocate),
79         NS_DISPATCH_NORMAL);
80   }
81 
IsLocal(bool * aIsLocal)82   virtual void IsLocal(bool* aIsLocal) override { *aIsLocal = true; }
83 
KeepWhenOffline(bool * aKeepWhenOffline)84   virtual void KeepWhenOffline(bool* aKeepWhenOffline) override {
85     *aKeepWhenOffline = true;
86   }
87 
ByteCountSent()88   virtual uint64_t ByteCountSent() override { return 0; }
ByteCountReceived()89   virtual uint64_t ByteCountReceived() override { return 0; }
90 
ServiceWatcher(DNSServiceRef aService,MDNSResponderOperator * aOperator)91   explicit ServiceWatcher(DNSServiceRef aService,
92                           MDNSResponderOperator* aOperator)
93       : mThread(nullptr),
94         mSts(nullptr),
95         mOperatorHolder(aOperator),
96         mService(aService),
97         mFD(nullptr),
98         mAttached(false) {
99     if (!gSocketTransportService) {
100       nsCOMPtr<nsISocketTransportService> sts =
101           do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID);
102     }
103   }
104 
Init()105   nsresult Init() {
106     MOZ_ASSERT(!OnSocketThread(), "on socket thread");
107     mThread = NS_GetCurrentThread();
108 
109     if (!mService) {
110       return NS_OK;
111     }
112 
113     if (!gSocketTransportService) {
114       return NS_ERROR_FAILURE;
115     }
116     mSts = gSocketTransportService;
117 
118     int osfd = DNSServiceRefSockFD(mService);
119     if (osfd == -1) {
120       return NS_ERROR_FAILURE;
121     }
122 
123     mFD = PR_ImportFile(osfd);
124     return PostEvent("MDNSResponderOperator::ServiceWatcher::OnMsgAttach",
125                      &ServiceWatcher::OnMsgAttach);
126   }
127 
Close()128   void Close() {
129     MOZ_ASSERT(!OnSocketThread(), "on socket thread");
130 
131     if (!gSocketTransportService) {
132       Deallocate();
133       return;
134     }
135 
136     PostEvent("MDNSResponderOperator::ServiceWatcher::OnMsgClose",
137               &ServiceWatcher::OnMsgClose);
138   }
139 
140  private:
141   ~ServiceWatcher() = default;
142 
Deallocate()143   void Deallocate() {
144     if (mService) {
145       DNSServiceRefDeallocate(mService);
146       mService = nullptr;
147     }
148     mOperatorHolder = nullptr;
149   }
150 
PostEvent(const char * aName,void (ServiceWatcher::* func)(void))151   nsresult PostEvent(const char* aName, void (ServiceWatcher::*func)(void)) {
152     return gSocketTransportService->Dispatch(
153         NewRunnableMethod(aName, this, func), NS_DISPATCH_NORMAL);
154   }
155 
OnMsgClose()156   void OnMsgClose() {
157     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
158 
159     if (NS_FAILED(mCondition)) {
160       return;
161     }
162 
163     // tear down socket. this signals the STS to detach our socket handler.
164     mCondition = NS_BINDING_ABORTED;
165 
166     // if we are attached, then socket transport service will call our
167     // OnSocketDetached method automatically. Otherwise, we have to call it
168     // (and thus close the socket) manually.
169     if (!mAttached) {
170       OnSocketDetached(mFD);
171     }
172   }
173 
OnMsgAttach()174   void OnMsgAttach() {
175     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
176 
177     if (NS_FAILED(mCondition)) {
178       return;
179     }
180 
181     mCondition = TryAttach();
182 
183     // if we hit an error while trying to attach then bail...
184     if (NS_FAILED(mCondition)) {
185       NS_ASSERTION(!mAttached, "should not be attached already");
186       OnSocketDetached(mFD);
187     }
188   }
189 
TryAttach()190   nsresult TryAttach() {
191     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
192 
193     nsresult rv;
194 
195     if (!gSocketTransportService) {
196       return NS_ERROR_FAILURE;
197     }
198 
199     //
200     // find out if it is going to be ok to attach another socket to the STS.
201     // if not then we have to wait for the STS to tell us that it is ok.
202     // the notification is asynchronous, which means that when we could be
203     // in a race to call AttachSocket once notified.  for this reason, when
204     // we get notified, we just re-enter this function.  as a result, we are
205     // sure to ask again before calling AttachSocket.  in this way we deal
206     // with the race condition.  though it isn't the most elegant solution,
207     // it is far simpler than trying to build a system that would guarantee
208     // FIFO ordering (which wouldn't even be that valuable IMO).  see bug
209     // 194402 for more info.
210     //
211     if (!gSocketTransportService->CanAttachSocket()) {
212       nsCOMPtr<nsIRunnable> event = NewRunnableMethod(
213           "MDNSResponderOperator::ServiceWatcher::OnMsgAttach", this,
214           &ServiceWatcher::OnMsgAttach);
215 
216       nsresult rv = gSocketTransportService->NotifyWhenCanAttachSocket(event);
217       if (NS_FAILED(rv)) {
218         return rv;
219       }
220     }
221 
222     //
223     // ok, we can now attach our socket to the STS for polling
224     //
225     rv = gSocketTransportService->AttachSocket(mFD, this);
226     if (NS_FAILED(rv)) {
227       return rv;
228     }
229 
230     mAttached = true;
231 
232     //
233     // now, configure our poll flags for listening...
234     //
235     mPollFlags = (PR_POLL_READ | PR_POLL_EXCEPT);
236 
237     return NS_OK;
238   }
239 
240   nsCOMPtr<nsIThread> mThread;
241   RefPtr<nsSocketTransportService> mSts;
242   RefPtr<MDNSResponderOperator> mOperatorHolder;
243   DNSServiceRef mService;
244   PRFileDesc* mFD;
245   bool mAttached;
246 };
247 
NS_IMPL_ISUPPORTS(MDNSResponderOperator::ServiceWatcher,nsISupports)248 NS_IMPL_ISUPPORTS(MDNSResponderOperator::ServiceWatcher, nsISupports)
249 
250 MDNSResponderOperator::MDNSResponderOperator()
251     : mService(nullptr),
252       mWatcher(nullptr),
253       mThread(NS_GetCurrentThread()),
254       mIsCancelled(false) {}
255 
~MDNSResponderOperator()256 MDNSResponderOperator::~MDNSResponderOperator() { Stop(); }
257 
Start()258 nsresult MDNSResponderOperator::Start() {
259   if (mIsCancelled) {
260     return NS_OK;
261   }
262 
263   if (IsServing()) {
264     Stop();
265   }
266 
267   return NS_OK;
268 }
269 
Stop()270 nsresult MDNSResponderOperator::Stop() { return ResetService(nullptr); }
271 
ResetService(DNSServiceRef aService)272 nsresult MDNSResponderOperator::ResetService(DNSServiceRef aService) {
273   nsresult rv;
274 
275   if (aService != mService) {
276     if (mWatcher) {
277       mWatcher->Close();
278       mWatcher = nullptr;
279     }
280 
281     if (aService) {
282       RefPtr<ServiceWatcher> watcher = new ServiceWatcher(aService, this);
283       if (NS_WARN_IF(NS_FAILED(rv = watcher->Init()))) {
284         return rv;
285       }
286       mWatcher = watcher;
287     }
288 
289     mService = aService;
290   }
291   return NS_OK;
292 }
293 
BrowseOperator(const nsACString & aServiceType,nsIDNSServiceDiscoveryListener * aListener)294 BrowseOperator::BrowseOperator(const nsACString& aServiceType,
295                                nsIDNSServiceDiscoveryListener* aListener)
296     : MDNSResponderOperator(),
297       mServiceType(aServiceType),
298       mListener(aListener) {}
299 
Start()300 nsresult BrowseOperator::Start() {
301   nsresult rv;
302   if (NS_WARN_IF(NS_FAILED(rv = MDNSResponderOperator::Start()))) {
303     return rv;
304   }
305 
306   DNSServiceRef service = nullptr;
307   DNSServiceErrorType err = DNSServiceBrowse(
308       &service, 0, kDNSServiceInterfaceIndexAny, mServiceType.get(), nullptr,
309       &BrowseReplyRunnable::Reply, this);
310   NS_WARNING_ASSERTION(kDNSServiceErr_NoError == err, "DNSServiceBrowse fail");
311 
312   if (mListener) {
313     if (kDNSServiceErr_NoError == err) {
314       mListener->OnDiscoveryStarted(mServiceType);
315     } else {
316       mListener->OnStartDiscoveryFailed(mServiceType, err);
317     }
318   }
319 
320   if (NS_WARN_IF(kDNSServiceErr_NoError != err)) {
321     return NS_ERROR_FAILURE;
322   }
323 
324   return ResetService(service);
325 }
326 
Stop()327 nsresult BrowseOperator::Stop() {
328   bool isServing = IsServing();
329   nsresult rv = MDNSResponderOperator::Stop();
330 
331   if (isServing && mListener) {
332     if (NS_SUCCEEDED(rv)) {
333       mListener->OnDiscoveryStopped(mServiceType);
334     } else {
335       mListener->OnStopDiscoveryFailed(mServiceType, static_cast<uint32_t>(rv));
336     }
337   }
338 
339   return rv;
340 }
341 
Reply(DNSServiceRef aSdRef,DNSServiceFlags aFlags,uint32_t aInterfaceIndex,DNSServiceErrorType aErrorCode,const nsACString & aServiceName,const nsACString & aRegType,const nsACString & aReplyDomain)342 void BrowseOperator::Reply(DNSServiceRef aSdRef, DNSServiceFlags aFlags,
343                            uint32_t aInterfaceIndex,
344                            DNSServiceErrorType aErrorCode,
345                            const nsACString& aServiceName,
346                            const nsACString& aRegType,
347                            const nsACString& aReplyDomain) {
348   MOZ_ASSERT(GetThread() == NS_GetCurrentThread());
349 
350   if (NS_WARN_IF(kDNSServiceErr_NoError != aErrorCode)) {
351     LOG_E("BrowseOperator::Reply (%d)", aErrorCode);
352     if (mListener) {
353       mListener->OnStartDiscoveryFailed(mServiceType, aErrorCode);
354     }
355     return;
356   }
357 
358   if (!mListener) {
359     return;
360   }
361   nsCOMPtr<nsIDNSServiceInfo> info = new nsDNSServiceInfo();
362 
363   if (NS_WARN_IF(!info)) {
364     return;
365   }
366   if (NS_WARN_IF(NS_FAILED(info->SetServiceName(aServiceName)))) {
367     return;
368   }
369   if (NS_WARN_IF(NS_FAILED(info->SetServiceType(aRegType)))) {
370     return;
371   }
372   if (NS_WARN_IF(NS_FAILED(info->SetDomainName(aReplyDomain)))) {
373     return;
374   }
375 
376   if (aFlags & kDNSServiceFlagsAdd) {
377     mListener->OnServiceFound(info);
378   } else {
379     mListener->OnServiceLost(info);
380   }
381 }
382 
RegisterOperator(nsIDNSServiceInfo * aServiceInfo,nsIDNSRegistrationListener * aListener)383 RegisterOperator::RegisterOperator(nsIDNSServiceInfo* aServiceInfo,
384                                    nsIDNSRegistrationListener* aListener)
385     : MDNSResponderOperator(),
386       mServiceInfo(aServiceInfo),
387       mListener(aListener) {}
388 
Start()389 nsresult RegisterOperator::Start() {
390   nsresult rv;
391 
392   rv = MDNSResponderOperator::Start();
393   if (NS_WARN_IF(NS_FAILED(rv))) {
394     return rv;
395   }
396   uint16_t port;
397   if (NS_WARN_IF(NS_FAILED(rv = mServiceInfo->GetPort(&port)))) {
398     return rv;
399   }
400   nsAutoCString type;
401   if (NS_WARN_IF(NS_FAILED(rv = mServiceInfo->GetServiceType(type)))) {
402     return rv;
403   }
404 
405   TXTRecordRef txtRecord;
406   char buf[TXT_BUFFER_SIZE] = {0};
407   TXTRecordCreate(&txtRecord, TXT_BUFFER_SIZE, buf);
408 
409   nsCOMPtr<nsIPropertyBag2> attributes;
410   if (NS_FAILED(rv = mServiceInfo->GetAttributes(getter_AddRefs(attributes)))) {
411     LOG_I("register: no attributes");
412   } else {
413     nsCOMPtr<nsISimpleEnumerator> enumerator;
414     if (NS_WARN_IF(NS_FAILED(
415             rv = attributes->GetEnumerator(getter_AddRefs(enumerator))))) {
416       return rv;
417     }
418 
419     bool hasMoreElements;
420     while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMoreElements)) &&
421            hasMoreElements) {
422       nsCOMPtr<nsISupports> element;
423       MOZ_ALWAYS_SUCCEEDS(enumerator->GetNext(getter_AddRefs(element)));
424       nsCOMPtr<nsIProperty> property = do_QueryInterface(element);
425       MOZ_ASSERT(property);
426 
427       nsAutoString name;
428       nsCOMPtr<nsIVariant> value;
429       MOZ_ALWAYS_SUCCEEDS(property->GetName(name));
430       MOZ_ALWAYS_SUCCEEDS(property->GetValue(getter_AddRefs(value)));
431 
432       nsAutoCString str;
433       if (NS_WARN_IF(NS_FAILED(value->GetAsACString(str)))) {
434         continue;
435       }
436 
437       TXTRecordSetValue(&txtRecord,
438                         /* it's safe because key name is ASCII only. */
439                         NS_LossyConvertUTF16toASCII(name).get(), str.Length(),
440                         str.get());
441     }
442   }
443 
444   nsAutoCString host;
445   nsAutoCString name;
446   nsAutoCString domain;
447 
448   DNSServiceRef service = nullptr;
449   DNSServiceErrorType err = DNSServiceRegister(
450       &service, 0, 0,
451       NS_SUCCEEDED(mServiceInfo->GetServiceName(name)) ? name.get() : nullptr,
452       type.get(),
453       NS_SUCCEEDED(mServiceInfo->GetDomainName(domain)) ? domain.get()
454                                                         : nullptr,
455       NS_SUCCEEDED(mServiceInfo->GetHost(host)) ? host.get() : nullptr,
456       NativeEndian::swapToNetworkOrder(port), TXTRecordGetLength(&txtRecord),
457       TXTRecordGetBytesPtr(&txtRecord), &RegisterReplyRunnable::Reply, this);
458   NS_WARNING_ASSERTION(kDNSServiceErr_NoError == err,
459                        "DNSServiceRegister fail");
460 
461   TXTRecordDeallocate(&txtRecord);
462 
463   if (NS_WARN_IF(kDNSServiceErr_NoError != err)) {
464     if (mListener) {
465       mListener->OnRegistrationFailed(mServiceInfo, err);
466     }
467     return NS_ERROR_FAILURE;
468   }
469 
470   return ResetService(service);
471 }
472 
Stop()473 nsresult RegisterOperator::Stop() {
474   bool isServing = IsServing();
475   nsresult rv = MDNSResponderOperator::Stop();
476 
477   if (isServing && mListener) {
478     if (NS_SUCCEEDED(rv)) {
479       mListener->OnServiceUnregistered(mServiceInfo);
480     } else {
481       mListener->OnUnregistrationFailed(mServiceInfo,
482                                         static_cast<uint32_t>(rv));
483     }
484   }
485 
486   return rv;
487 }
488 
Reply(DNSServiceRef aSdRef,DNSServiceFlags aFlags,DNSServiceErrorType aErrorCode,const nsACString & aName,const nsACString & aRegType,const nsACString & aDomain)489 void RegisterOperator::Reply(DNSServiceRef aSdRef, DNSServiceFlags aFlags,
490                              DNSServiceErrorType aErrorCode,
491                              const nsACString& aName,
492                              const nsACString& aRegType,
493                              const nsACString& aDomain) {
494   MOZ_ASSERT(GetThread() == NS_GetCurrentThread());
495 
496   if (kDNSServiceErr_NoError != aErrorCode) {
497     LOG_E("RegisterOperator::Reply (%d)", aErrorCode);
498   }
499 
500   if (!mListener) {
501     return;
502   }
503   nsCOMPtr<nsIDNSServiceInfo> info = new nsDNSServiceInfo(mServiceInfo);
504   if (NS_WARN_IF(NS_FAILED(info->SetServiceName(aName)))) {
505     return;
506   }
507   if (NS_WARN_IF(NS_FAILED(info->SetServiceType(aRegType)))) {
508     return;
509   }
510   if (NS_WARN_IF(NS_FAILED(info->SetDomainName(aDomain)))) {
511     return;
512   }
513 
514   if (kDNSServiceErr_NoError == aErrorCode) {
515     if (aFlags & kDNSServiceFlagsAdd) {
516       mListener->OnServiceRegistered(info);
517     } else {
518       // If a successfully-registered name later suffers a name conflict
519       // or similar problem and has to be deregistered, the callback will
520       // be invoked with the kDNSServiceFlagsAdd flag not set.
521       LOG_E("RegisterOperator::Reply: deregister");
522       if (NS_WARN_IF(NS_FAILED(Stop()))) {
523         return;
524       }
525     }
526   } else {
527     mListener->OnRegistrationFailed(info, aErrorCode);
528   }
529 }
530 
ResolveOperator(nsIDNSServiceInfo * aServiceInfo,nsIDNSServiceResolveListener * aListener)531 ResolveOperator::ResolveOperator(nsIDNSServiceInfo* aServiceInfo,
532                                  nsIDNSServiceResolveListener* aListener)
533     : MDNSResponderOperator(),
534       mServiceInfo(aServiceInfo),
535       mListener(aListener) {}
536 
Start()537 nsresult ResolveOperator::Start() {
538   nsresult rv;
539   if (NS_WARN_IF(NS_FAILED(rv = MDNSResponderOperator::Start()))) {
540     return rv;
541   }
542 
543   nsAutoCString name;
544   mServiceInfo->GetServiceName(name);
545   nsAutoCString type;
546   mServiceInfo->GetServiceType(type);
547   nsAutoCString domain;
548   mServiceInfo->GetDomainName(domain);
549 
550   LOG_I("Resolve: (%s), (%s), (%s)", name.get(), type.get(), domain.get());
551 
552   DNSServiceRef service = nullptr;
553   DNSServiceErrorType err = DNSServiceResolve(
554       &service, 0, kDNSServiceInterfaceIndexAny, name.get(), type.get(),
555       domain.get(), (DNSServiceResolveReply)&ResolveReplyRunnable::Reply, this);
556 
557   if (NS_WARN_IF(kDNSServiceErr_NoError != err)) {
558     if (mListener) {
559       mListener->OnResolveFailed(mServiceInfo, err);
560     }
561     return NS_ERROR_FAILURE;
562   }
563 
564   return ResetService(service);
565 }
566 
Reply(DNSServiceRef aSdRef,DNSServiceFlags aFlags,uint32_t aInterfaceIndex,DNSServiceErrorType aErrorCode,const nsACString & aFullName,const nsACString & aHostTarget,uint16_t aPort,uint16_t aTxtLen,const unsigned char * aTxtRecord)567 void ResolveOperator::Reply(DNSServiceRef aSdRef, DNSServiceFlags aFlags,
568                             uint32_t aInterfaceIndex,
569                             DNSServiceErrorType aErrorCode,
570                             const nsACString& aFullName,
571                             const nsACString& aHostTarget, uint16_t aPort,
572                             uint16_t aTxtLen, const unsigned char* aTxtRecord) {
573   MOZ_ASSERT(GetThread() == NS_GetCurrentThread());
574 
575   auto guard = MakeScopeExit([&] { Unused << NS_WARN_IF(NS_FAILED(Stop())); });
576 
577   if (NS_WARN_IF(kDNSServiceErr_NoError != aErrorCode)) {
578     LOG_E("ResolveOperator::Reply (%d)", aErrorCode);
579     return;
580   }
581 
582   // Resolve TXT record
583   int count = TXTRecordGetCount(aTxtLen, aTxtRecord);
584   LOG_I("resolve: txt count = %d, len = %d", count, aTxtLen);
585   nsCOMPtr<nsIWritablePropertyBag2> attributes = new nsHashPropertyBag();
586   if (NS_WARN_IF(!attributes)) {
587     return;
588   }
589   if (count) {
590     for (int i = 0; i < count; ++i) {
591       char key[TXT_BUFFER_SIZE] = {'\0'};
592       uint8_t vSize = 0;
593       const void* value = nullptr;
594       if (kDNSServiceErr_NoError !=
595           TXTRecordGetItemAtIndex(aTxtLen, aTxtRecord, i, TXT_BUFFER_SIZE, key,
596                                   &vSize, &value)) {
597         break;
598       }
599 
600       nsAutoCString str(reinterpret_cast<const char*>(value), vSize);
601       LOG_I("resolve TXT: (%d) %s=%s", vSize, key, str.get());
602 
603       if (NS_WARN_IF(NS_FAILED(attributes->SetPropertyAsACString(
604               /* it's safe to convert because key name is ASCII only. */
605               NS_ConvertASCIItoUTF16(key), str)))) {
606         break;
607       }
608     }
609   }
610 
611   if (!mListener) {
612     return;
613   }
614   nsCOMPtr<nsIDNSServiceInfo> info = new nsDNSServiceInfo(mServiceInfo);
615   if (NS_WARN_IF(NS_FAILED(info->SetHost(aHostTarget)))) {
616     return;
617   }
618   if (NS_WARN_IF(NS_FAILED(info->SetPort(aPort)))) {
619     return;
620   }
621   if (NS_WARN_IF(NS_FAILED(info->SetAttributes(attributes)))) {
622     return;
623   }
624 
625   if (kDNSServiceErr_NoError == aErrorCode) {
626     GetAddrInfor(info);
627   } else {
628     mListener->OnResolveFailed(info, aErrorCode);
629     Unused << NS_WARN_IF(NS_FAILED(Stop()));
630   }
631 }
632 
GetAddrInfor(nsIDNSServiceInfo * aServiceInfo)633 void ResolveOperator::GetAddrInfor(nsIDNSServiceInfo* aServiceInfo) {
634   RefPtr<GetAddrInfoOperator> getAddreOp =
635       new GetAddrInfoOperator(aServiceInfo, mListener);
636   Unused << NS_WARN_IF(NS_FAILED(getAddreOp->Start()));
637 }
638 
GetAddrInfoOperator(nsIDNSServiceInfo * aServiceInfo,nsIDNSServiceResolveListener * aListener)639 GetAddrInfoOperator::GetAddrInfoOperator(
640     nsIDNSServiceInfo* aServiceInfo, nsIDNSServiceResolveListener* aListener)
641     : MDNSResponderOperator(),
642       mServiceInfo(aServiceInfo),
643       mListener(aListener) {}
644 
Start()645 nsresult GetAddrInfoOperator::Start() {
646   nsresult rv;
647   if (NS_WARN_IF(NS_FAILED(rv = MDNSResponderOperator::Start()))) {
648     return rv;
649   }
650 
651   nsAutoCString host;
652   mServiceInfo->GetHost(host);
653 
654   LOG_I("GetAddrInfo: (%s)", host.get());
655 
656   DNSServiceRef service = nullptr;
657   DNSServiceErrorType err = DNSServiceGetAddrInfo(
658       &service, kDNSServiceFlagsForceMulticast, kDNSServiceInterfaceIndexAny,
659       kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6, host.get(),
660       (DNSServiceGetAddrInfoReply)&GetAddrInfoReplyRunnable::Reply, this);
661 
662   if (NS_WARN_IF(kDNSServiceErr_NoError != err)) {
663     if (mListener) {
664       mListener->OnResolveFailed(mServiceInfo, err);
665     }
666     return NS_ERROR_FAILURE;
667   }
668 
669   return ResetService(service);
670 }
671 
Reply(DNSServiceRef aSdRef,DNSServiceFlags aFlags,uint32_t aInterfaceIndex,DNSServiceErrorType aErrorCode,const nsACString & aHostName,const NetAddr & aAddress,uint32_t aTTL)672 void GetAddrInfoOperator::Reply(DNSServiceRef aSdRef, DNSServiceFlags aFlags,
673                                 uint32_t aInterfaceIndex,
674                                 DNSServiceErrorType aErrorCode,
675                                 const nsACString& aHostName,
676                                 const NetAddr& aAddress, uint32_t aTTL) {
677   MOZ_ASSERT(GetThread() == NS_GetCurrentThread());
678 
679   auto guard = MakeScopeExit([&] { Unused << NS_WARN_IF(NS_FAILED(Stop())); });
680 
681   if (NS_WARN_IF(kDNSServiceErr_NoError != aErrorCode)) {
682     LOG_E("GetAddrInfoOperator::Reply (%d)", aErrorCode);
683     return;
684   }
685 
686   if (!mListener) {
687     return;
688   }
689 
690   NetAddr addr = aAddress;
691   nsCOMPtr<nsINetAddr> address = new nsNetAddr(&addr);
692   nsCString addressStr;
693   if (NS_WARN_IF(NS_FAILED(address->GetAddress(addressStr)))) {
694     return;
695   }
696 
697   nsCOMPtr<nsIDNSServiceInfo> info = new nsDNSServiceInfo(mServiceInfo);
698   if (NS_WARN_IF(NS_FAILED(info->SetAddress(addressStr)))) {
699     return;
700   }
701 
702   /**
703    * |kDNSServiceFlagsMoreComing| means this callback will be one or more
704    * callback events later, so this instance should be kept alive until all
705    * follow-up events are processed.
706    */
707   if (aFlags & kDNSServiceFlagsMoreComing) {
708     guard.release();
709   }
710 
711   if (kDNSServiceErr_NoError == aErrorCode) {
712     mListener->OnServiceResolved(info);
713   } else {
714     mListener->OnResolveFailed(info, aErrorCode);
715   }
716 }
717 
718 }  // namespace net
719 }  // namespace mozilla
720