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