netwerk/base/src/nsUDPSocket.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:ec38a9184312
1 /* vim:set ts=2 sw=2 et cindent: */
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 "mozilla/Attributes.h"
7 #include "mozilla/Endian.h"
8 #include "mozilla/dom/TypedArray.h"
9 #include "mozilla/HoldDropJSObjects.h"
10
11 #include "nsSocketTransport2.h"
12 #include "nsUDPSocket.h"
13 #include "nsProxyRelease.h"
14 #include "nsAutoPtr.h"
15 #include "nsError.h"
16 #include "nsNetCID.h"
17 #include "prnetdb.h"
18 #include "prio.h"
19 #include "nsNetAddr.h"
20 #include "nsNetSegmentUtils.h"
21 #include "NetworkActivityMonitor.h"
22 #include "nsStreamUtils.h"
23 #include "nsIPipe.h"
24 #include "prerror.h"
25 #include "nsThreadUtils.h"
26 #include "nsIDNSRecord.h"
27 #include "nsIDNSService.h"
28 #include "nsICancelable.h"
29
30 using namespace mozilla::net;
31 using namespace mozilla;
32
33 static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID);
34
35 //-----------------------------------------------------------------------------
36
37 typedef void (nsUDPSocket:: *nsUDPSocketFunc)(void);
38
39 static nsresult
40 PostEvent(nsUDPSocket *s, nsUDPSocketFunc func)
41 {
42 nsCOMPtr<nsIRunnable> ev = NS_NewRunnableMethod(s, func);
43
44 if (!gSocketTransportService)
45 return NS_ERROR_FAILURE;
46
47 return gSocketTransportService->Dispatch(ev, NS_DISPATCH_NORMAL);
48 }
49
50 static nsresult
51 ResolveHost(const nsACString &host, nsIDNSListener *listener)
52 {
53 nsresult rv;
54
55 nsCOMPtr<nsIDNSService> dns =
56 do_GetService("@mozilla.org/network/dns-service;1", &rv);
57 if (NS_FAILED(rv)) {
58 return rv;
59 }
60
61 nsCOMPtr<nsICancelable> tmpOutstanding;
62 return dns->AsyncResolve(host, 0, listener, nullptr,
63 getter_AddRefs(tmpOutstanding));
64
65 }
66
67 //-----------------------------------------------------------------------------
68 // nsUDPOutputStream impl
69 //-----------------------------------------------------------------------------
70 NS_IMPL_ISUPPORTS(nsUDPOutputStream, nsIOutputStream)
71
72 nsUDPOutputStream::nsUDPOutputStream(nsUDPSocket* aSocket,
73 PRFileDesc* aFD,
74 PRNetAddr& aPrClientAddr)
75 : mSocket(aSocket)
76 , mFD(aFD)
77 , mPrClientAddr(aPrClientAddr)
78 , mIsClosed(false)
79 {
80 }
81
82 nsUDPOutputStream::~nsUDPOutputStream()
83 {
84 }
85
86 /* void close (); */
87 NS_IMETHODIMP nsUDPOutputStream::Close()
88 {
89 if (mIsClosed)
90 return NS_BASE_STREAM_CLOSED;
91
92 mIsClosed = true;
93 return NS_OK;
94 }
95
96 /* void flush (); */
97 NS_IMETHODIMP nsUDPOutputStream::Flush()
98 {
99 return NS_OK;
100 }
101
102 /* unsigned long write (in string aBuf, in unsigned long aCount); */
103 NS_IMETHODIMP nsUDPOutputStream::Write(const char * aBuf, uint32_t aCount, uint32_t *_retval)
104 {
105 if (mIsClosed)
106 return NS_BASE_STREAM_CLOSED;
107
108 *_retval = 0;
109 int32_t count = PR_SendTo(mFD, aBuf, aCount, 0, &mPrClientAddr, PR_INTERVAL_NO_WAIT);
110 if (count < 0) {
111 PRErrorCode code = PR_GetError();
112 return ErrorAccordingToNSPR(code);
113 }
114
115 *_retval = count;
116
117 mSocket->AddOutputBytes(count);
118
119 return NS_OK;
120 }
121
122 /* unsigned long writeFrom (in nsIInputStream aFromStream, in unsigned long aCount); */
123 NS_IMETHODIMP nsUDPOutputStream::WriteFrom(nsIInputStream *aFromStream, uint32_t aCount, uint32_t *_retval)
124 {
125 return NS_ERROR_NOT_IMPLEMENTED;
126 }
127
128 /* [noscript] unsigned long writeSegments (in nsReadSegmentFun aReader, in voidPtr aClosure, in unsigned long aCount); */
129 NS_IMETHODIMP nsUDPOutputStream::WriteSegments(nsReadSegmentFun aReader, void *aClosure, uint32_t aCount, uint32_t *_retval)
130 {
131 return NS_ERROR_NOT_IMPLEMENTED;
132 }
133
134 /* boolean isNonBlocking (); */
135 NS_IMETHODIMP nsUDPOutputStream::IsNonBlocking(bool *_retval)
136 {
137 *_retval = true;
138 return NS_OK;
139 }
140
141 //-----------------------------------------------------------------------------
142 // nsUDPMessage impl
143 //-----------------------------------------------------------------------------
144 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsUDPMessage)
145 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsUDPMessage)
146
147 NS_IMPL_CYCLE_COLLECTION_CLASS(nsUDPMessage)
148
149 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsUDPMessage)
150 NS_INTERFACE_MAP_ENTRY(nsISupports)
151 NS_INTERFACE_MAP_ENTRY(nsIUDPMessage)
152 NS_INTERFACE_MAP_END
153
154 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsUDPMessage)
155 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mJsobj)
156 NS_IMPL_CYCLE_COLLECTION_TRACE_END
157
158 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsUDPMessage)
159 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
160 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
161
162 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsUDPMessage)
163 tmp->mJsobj = nullptr;
164 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
165
166 nsUDPMessage::nsUDPMessage(NetAddr* aAddr,
167 nsIOutputStream* aOutputStream,
168 FallibleTArray<uint8_t>& aData)
169 : mOutputStream(aOutputStream)
170 {
171 memcpy(&mAddr, aAddr, sizeof(NetAddr));
172 aData.SwapElements(mData);
173 }
174
175 nsUDPMessage::~nsUDPMessage()
176 {
177 mozilla::DropJSObjects(this);
178 }
179
180 /* readonly attribute nsINetAddr from; */
181 NS_IMETHODIMP
182 nsUDPMessage::GetFromAddr(nsINetAddr * *aFromAddr)
183 {
184 NS_ENSURE_ARG_POINTER(aFromAddr);
185
186 nsCOMPtr<nsINetAddr> result = new nsNetAddr(&mAddr);
187 result.forget(aFromAddr);
188
189 return NS_OK;
190 }
191
192 /* readonly attribute ACString data; */
193 NS_IMETHODIMP
194 nsUDPMessage::GetData(nsACString & aData)
195 {
196 aData.Assign(reinterpret_cast<const char*>(mData.Elements()), mData.Length());
197 return NS_OK;
198 }
199
200 /* readonly attribute nsIOutputStream outputStream; */
201 NS_IMETHODIMP
202 nsUDPMessage::GetOutputStream(nsIOutputStream * *aOutputStream)
203 {
204 NS_ENSURE_ARG_POINTER(aOutputStream);
205 NS_IF_ADDREF(*aOutputStream = mOutputStream);
206 return NS_OK;
207 }
208
209 /* readonly attribute jsval rawData; */
210 NS_IMETHODIMP
211 nsUDPMessage::GetRawData(JSContext* cx,
212 JS::MutableHandleValue aRawData)
213 {
214 if(!mJsobj){
215 mJsobj = mozilla::dom::Uint8Array::Create(cx, nullptr, mData.Length(), mData.Elements());
216 mozilla::HoldJSObjects(this);
217 }
218 aRawData.setObject(*mJsobj);
219 return NS_OK;
220 }
221
222 /* [noscript, notxpcom, nostdcall] Uint8ArrayRef getDataAsTArray(); */
223 FallibleTArray<uint8_t>&
224 nsUDPMessage::GetDataAsTArray()
225 {
226 return mData;
227 }
228
229 //-----------------------------------------------------------------------------
230 // nsUDPSocket
231 //-----------------------------------------------------------------------------
232
233 nsUDPSocket::nsUDPSocket()
234 : mLock("nsUDPSocket.mLock")
235 , mFD(nullptr)
236 , mAttached(false)
237 , mByteReadCount(0)
238 , mByteWriteCount(0)
239 {
240 mAddr.raw.family = PR_AF_UNSPEC;
241 // we want to be able to access the STS directly, and it may not have been
242 // constructed yet. the STS constructor sets gSocketTransportService.
243 if (!gSocketTransportService)
244 {
245 // This call can fail if we're offline, for example.
246 nsCOMPtr<nsISocketTransportService> sts =
247 do_GetService(kSocketTransportServiceCID);
248 }
249
250 mSts = gSocketTransportService;
251 MOZ_COUNT_CTOR(nsUDPSocket);
252 }
253
254 nsUDPSocket::~nsUDPSocket()
255 {
256 Close(); // just in case :)
257
258 MOZ_COUNT_DTOR(nsUDPSocket);
259 }
260
261 void
262 nsUDPSocket::AddOutputBytes(uint64_t aBytes)
263 {
264 mByteWriteCount += aBytes;
265 }
266
267 void
268 nsUDPSocket::OnMsgClose()
269 {
270 SOCKET_LOG(("nsUDPSocket::OnMsgClose [this=%p]\n", this));
271
272 if (NS_FAILED(mCondition))
273 return;
274
275 // tear down socket. this signals the STS to detach our socket handler.
276 mCondition = NS_BINDING_ABORTED;
277
278 // if we are attached, then socket transport service will call our
279 // OnSocketDetached method automatically. Otherwise, we have to call it
280 // (and thus close the socket) manually.
281 if (!mAttached)
282 OnSocketDetached(mFD);
283 }
284
285 void
286 nsUDPSocket::OnMsgAttach()
287 {
288 SOCKET_LOG(("nsUDPSocket::OnMsgAttach [this=%p]\n", this));
289
290 if (NS_FAILED(mCondition))
291 return;
292
293 mCondition = TryAttach();
294
295 // if we hit an error while trying to attach then bail...
296 if (NS_FAILED(mCondition))
297 {
298 NS_ASSERTION(!mAttached, "should not be attached already");
299 OnSocketDetached(mFD);
300 }
301 }
302
303 nsresult
304 nsUDPSocket::TryAttach()
305 {
306 nsresult rv;
307
308 if (!gSocketTransportService)
309 return NS_ERROR_FAILURE;
310
311 //
312 // find out if it is going to be ok to attach another socket to the STS.
313 // if not then we have to wait for the STS to tell us that it is ok.
314 // the notification is asynchronous, which means that when we could be
315 // in a race to call AttachSocket once notified. for this reason, when
316 // we get notified, we just re-enter this function. as a result, we are
317 // sure to ask again before calling AttachSocket. in this way we deal
318 // with the race condition. though it isn't the most elegant solution,
319 // it is far simpler than trying to build a system that would guarantee
320 // FIFO ordering (which wouldn't even be that valuable IMO). see bug
321 // 194402 for more info.
322 //
323 if (!gSocketTransportService->CanAttachSocket())
324 {
325 nsCOMPtr<nsIRunnable> event =
326 NS_NewRunnableMethod(this, &nsUDPSocket::OnMsgAttach);
327
328 nsresult rv = gSocketTransportService->NotifyWhenCanAttachSocket(event);
329 if (NS_FAILED(rv))
330 return rv;
331 }
332
333 //
334 // ok, we can now attach our socket to the STS for polling
335 //
336 rv = gSocketTransportService->AttachSocket(mFD, this);
337 if (NS_FAILED(rv))
338 return rv;
339
340 mAttached = true;
341
342 //
343 // now, configure our poll flags for listening...
344 //
345 mPollFlags = (PR_POLL_READ | PR_POLL_EXCEPT);
346 return NS_OK;
347 }
348
349 namespace {
350 //-----------------------------------------------------------------------------
351 // UDPMessageProxy
352 //-----------------------------------------------------------------------------
353 class UDPMessageProxy MOZ_FINAL : public nsIUDPMessage
354 {
355 public:
356 UDPMessageProxy(NetAddr* aAddr,
357 nsIOutputStream* aOutputStream,
358 FallibleTArray<uint8_t>& aData)
359 : mOutputStream(aOutputStream)
360 {
361 memcpy(&mAddr, aAddr, sizeof(NetAddr));
362 aData.SwapElements(mData);
363 }
364
365 NS_DECL_THREADSAFE_ISUPPORTS
366 NS_DECL_NSIUDPMESSAGE
367
368 private:
369 NetAddr mAddr;
370 nsCOMPtr<nsIOutputStream> mOutputStream;
371 FallibleTArray<uint8_t> mData;
372 };
373
374 NS_IMPL_ISUPPORTS(UDPMessageProxy, nsIUDPMessage)
375
376 /* readonly attribute nsINetAddr from; */
377 NS_IMETHODIMP
378 UDPMessageProxy::GetFromAddr(nsINetAddr * *aFromAddr)
379 {
380 NS_ENSURE_ARG_POINTER(aFromAddr);
381
382 nsCOMPtr<nsINetAddr> result = new nsNetAddr(&mAddr);
383 result.forget(aFromAddr);
384
385 return NS_OK;
386 }
387
388 /* readonly attribute ACString data; */
389 NS_IMETHODIMP
390 UDPMessageProxy::GetData(nsACString & aData)
391 {
392 return NS_ERROR_NOT_IMPLEMENTED;
393 }
394
395 /* [noscript, notxpcom, nostdcall] Uint8TArrayRef getDataAsTArray(); */
396 FallibleTArray<uint8_t>&
397 UDPMessageProxy::GetDataAsTArray()
398 {
399 return mData;
400 }
401
402 /* readonly attribute jsval rawData; */
403 NS_IMETHODIMP
404 UDPMessageProxy::GetRawData(JSContext* cx,
405 JS::MutableHandleValue aRawData)
406 {
407 return NS_ERROR_NOT_IMPLEMENTED;
408 }
409
410 /* readonly attribute nsIOutputStream outputStream; */
411 NS_IMETHODIMP
412 UDPMessageProxy::GetOutputStream(nsIOutputStream * *aOutputStream)
413 {
414 NS_ENSURE_ARG_POINTER(aOutputStream);
415 NS_IF_ADDREF(*aOutputStream = mOutputStream);
416 return NS_OK;
417 }
418
419 } //anonymous namespace
420
421 //-----------------------------------------------------------------------------
422 // nsUDPSocket::nsASocketHandler
423 //-----------------------------------------------------------------------------
424
425 void
426 nsUDPSocket::OnSocketReady(PRFileDesc *fd, int16_t outFlags)
427 {
428 NS_ASSERTION(NS_SUCCEEDED(mCondition), "oops");
429 NS_ASSERTION(mFD == fd, "wrong file descriptor");
430 NS_ASSERTION(outFlags != -1, "unexpected timeout condition reached");
431
432 if (outFlags & (PR_POLL_ERR | PR_POLL_HUP | PR_POLL_NVAL))
433 {
434 NS_WARNING("error polling on listening socket");
435 mCondition = NS_ERROR_UNEXPECTED;
436 return;
437 }
438
439 PRNetAddr prClientAddr;
440 uint32_t count;
441 char buff[1500];
442 count = PR_RecvFrom(mFD, buff, sizeof(buff), 0, &prClientAddr, PR_INTERVAL_NO_WAIT);
443
444 if (count < 1) {
445 NS_WARNING("error of recvfrom on UDP socket");
446 mCondition = NS_ERROR_UNEXPECTED;
447 return;
448 }
449 mByteReadCount += count;
450
451 FallibleTArray<uint8_t> data;
452 if(!data.AppendElements(buff, count)){
453 mCondition = NS_ERROR_UNEXPECTED;
454 return;
455 }
456
457 nsCOMPtr<nsIAsyncInputStream> pipeIn;
458 nsCOMPtr<nsIAsyncOutputStream> pipeOut;
459
460 uint32_t segsize = 1400;
461 uint32_t segcount = 0;
462 net_ResolveSegmentParams(segsize, segcount);
463 nsresult rv = NS_NewPipe2(getter_AddRefs(pipeIn), getter_AddRefs(pipeOut),
464 true, true, segsize, segcount);
465
466 if (NS_FAILED(rv)) {
467 return;
468 }
469
470 nsRefPtr<nsUDPOutputStream> os = new nsUDPOutputStream(this, mFD, prClientAddr);
471 rv = NS_AsyncCopy(pipeIn, os, mSts,
472 NS_ASYNCCOPY_VIA_READSEGMENTS, 1400);
473
474 if (NS_FAILED(rv)) {
475 return;
476 }
477
478 NetAddr netAddr;
479 PRNetAddrToNetAddr(&prClientAddr, &netAddr);
480 nsCOMPtr<nsIUDPMessage> message = new UDPMessageProxy(&netAddr, pipeOut, data);
481 mListener->OnPacketReceived(this, message);
482 }
483
484 void
485 nsUDPSocket::OnSocketDetached(PRFileDesc *fd)
486 {
487 // force a failure condition if none set; maybe the STS is shutting down :-/
488 if (NS_SUCCEEDED(mCondition))
489 mCondition = NS_ERROR_ABORT;
490
491 if (mFD)
492 {
493 NS_ASSERTION(mFD == fd, "wrong file descriptor");
494 PR_Close(mFD);
495 mFD = nullptr;
496 }
497
498 if (mListener)
499 {
500 // need to atomically clear mListener. see our Close() method.
501 nsCOMPtr<nsIUDPSocketListener> listener;
502 {
503 MutexAutoLock lock(mLock);
504 mListener.swap(listener);
505 }
506
507 if (listener) {
508 listener->OnStopListening(this, mCondition);
509 NS_ProxyRelease(mListenerTarget, listener);
510 }
511 }
512 }
513
514 void
515 nsUDPSocket::IsLocal(bool *aIsLocal)
516 {
517 // If bound to loopback, this UDP socket only accepts local connections.
518 *aIsLocal = mAddr.raw.family == nsINetAddr::FAMILY_LOCAL;
519 }
520
521 //-----------------------------------------------------------------------------
522 // nsSocket::nsISupports
523 //-----------------------------------------------------------------------------
524
525 NS_IMPL_ISUPPORTS(nsUDPSocket, nsIUDPSocket)
526
527
528 //-----------------------------------------------------------------------------
529 // nsSocket::nsISocket
530 //-----------------------------------------------------------------------------
531
532 NS_IMETHODIMP
533 nsUDPSocket::Init(int32_t aPort, bool aLoopbackOnly)
534 {
535 NetAddr addr;
536
537 if (aPort < 0)
538 aPort = 0;
539
540 addr.raw.family = AF_INET;
541 addr.inet.port = htons(aPort);
542
543 if (aLoopbackOnly)
544 addr.inet.ip = htonl(INADDR_LOOPBACK);
545 else
546 addr.inet.ip = htonl(INADDR_ANY);
547
548 return InitWithAddress(&addr);
549 }
550
551 NS_IMETHODIMP
552 nsUDPSocket::InitWithAddress(const NetAddr *aAddr)
553 {
554 NS_ENSURE_TRUE(mFD == nullptr, NS_ERROR_ALREADY_INITIALIZED);
555
556 //
557 // configure listening socket...
558 //
559
560 mFD = PR_OpenUDPSocket(aAddr->raw.family);
561 if (!mFD)
562 {
563 NS_WARNING("unable to create UDP socket");
564 return NS_ERROR_FAILURE;
565 }
566
567 uint16_t port;
568 if (NS_FAILED(net::GetPort(aAddr, &port))) {
569 NS_WARNING("invalid bind address");
570 goto fail;
571 }
572
573 PRSocketOptionData opt;
574
575 // Linux kernel will sometimes hand out a used port if we bind
576 // to port 0 with SO_REUSEADDR
577 if (port) {
578 opt.option = PR_SockOpt_Reuseaddr;
579 opt.value.reuse_addr = true;
580 PR_SetSocketOption(mFD, &opt);
581 }
582
583 opt.option = PR_SockOpt_Nonblocking;
584 opt.value.non_blocking = true;
585 PR_SetSocketOption(mFD, &opt);
586
587 PRNetAddr addr;
588 PR_InitializeNetAddr(PR_IpAddrAny, 0, &addr);
589 NetAddrToPRNetAddr(aAddr, &addr);
590
591 if (PR_Bind(mFD, &addr) != PR_SUCCESS)
592 {
593 NS_WARNING("failed to bind socket");
594 goto fail;
595 }
596
597 // get the resulting socket address, which may be different than what
598 // we passed to bind.
599 if (PR_GetSockName(mFD, &addr) != PR_SUCCESS)
600 {
601 NS_WARNING("cannot get socket name");
602 goto fail;
603 }
604
605 PRNetAddrToNetAddr(&addr, &mAddr);
606
607 // create proxy via NetworkActivityMonitor
608 NetworkActivityMonitor::AttachIOLayer(mFD);
609
610 // wait until AsyncListen is called before polling the socket for
611 // client connections.
612 return NS_OK;
613
614 fail:
615 Close();
616 return NS_ERROR_FAILURE;
617 }
618
619 NS_IMETHODIMP
620 nsUDPSocket::Close()
621 {
622 {
623 MutexAutoLock lock(mLock);
624 // we want to proxy the close operation to the socket thread if a listener
625 // has been set. otherwise, we should just close the socket here...
626 if (!mListener)
627 {
628 if (mFD)
629 {
630 PR_Close(mFD);
631 mFD = nullptr;
632 }
633 return NS_OK;
634 }
635 }
636 return PostEvent(this, &nsUDPSocket::OnMsgClose);
637 }
638
639 NS_IMETHODIMP
640 nsUDPSocket::GetPort(int32_t *aResult)
641 {
642 // no need to enter the lock here
643 uint16_t result;
644 nsresult rv = net::GetPort(&mAddr, &result);
645 *aResult = static_cast<int32_t>(result);
646 return rv;
647 }
648
649 NS_IMETHODIMP
650 nsUDPSocket::GetAddress(NetAddr *aResult)
651 {
652 // no need to enter the lock here
653 memcpy(aResult, &mAddr, sizeof(mAddr));
654 return NS_OK;
655 }
656
657 namespace {
658 //-----------------------------------------------------------------------------
659 // SocketListenerProxy
660 //-----------------------------------------------------------------------------
661 class SocketListenerProxy MOZ_FINAL : public nsIUDPSocketListener
662 {
663 public:
664 SocketListenerProxy(nsIUDPSocketListener* aListener)
665 : mListener(new nsMainThreadPtrHolder<nsIUDPSocketListener>(aListener))
666 , mTargetThread(do_GetCurrentThread())
667 { }
668
669 NS_DECL_THREADSAFE_ISUPPORTS
670 NS_DECL_NSIUDPSOCKETLISTENER
671
672 class OnPacketReceivedRunnable : public nsRunnable
673 {
674 public:
675 OnPacketReceivedRunnable(const nsMainThreadPtrHandle<nsIUDPSocketListener>& aListener,
676 nsIUDPSocket* aSocket,
677 nsIUDPMessage* aMessage)
678 : mListener(aListener)
679 , mSocket(aSocket)
680 , mMessage(aMessage)
681 { }
682
683 NS_DECL_NSIRUNNABLE
684
685 private:
686 nsMainThreadPtrHandle<nsIUDPSocketListener> mListener;
687 nsCOMPtr<nsIUDPSocket> mSocket;
688 nsCOMPtr<nsIUDPMessage> mMessage;
689 };
690
691 class OnStopListeningRunnable : public nsRunnable
692 {
693 public:
694 OnStopListeningRunnable(const nsMainThreadPtrHandle<nsIUDPSocketListener>& aListener,
695 nsIUDPSocket* aSocket,
696 nsresult aStatus)
697 : mListener(aListener)
698 , mSocket(aSocket)
699 , mStatus(aStatus)
700 { }
701
702 NS_DECL_NSIRUNNABLE
703
704 private:
705 nsMainThreadPtrHandle<nsIUDPSocketListener> mListener;
706 nsCOMPtr<nsIUDPSocket> mSocket;
707 nsresult mStatus;
708 };
709
710 private:
711 nsMainThreadPtrHandle<nsIUDPSocketListener> mListener;
712 nsCOMPtr<nsIEventTarget> mTargetThread;
713 };
714
715 NS_IMPL_ISUPPORTS(SocketListenerProxy,
716 nsIUDPSocketListener)
717
718 NS_IMETHODIMP
719 SocketListenerProxy::OnPacketReceived(nsIUDPSocket* aSocket,
720 nsIUDPMessage* aMessage)
721 {
722 nsRefPtr<OnPacketReceivedRunnable> r =
723 new OnPacketReceivedRunnable(mListener, aSocket, aMessage);
724 return mTargetThread->Dispatch(r, NS_DISPATCH_NORMAL);
725 }
726
727 NS_IMETHODIMP
728 SocketListenerProxy::OnStopListening(nsIUDPSocket* aSocket,
729 nsresult aStatus)
730 {
731 nsRefPtr<OnStopListeningRunnable> r =
732 new OnStopListeningRunnable(mListener, aSocket, aStatus);
733 return mTargetThread->Dispatch(r, NS_DISPATCH_NORMAL);
734 }
735
736 NS_IMETHODIMP
737 SocketListenerProxy::OnPacketReceivedRunnable::Run()
738 {
739 NetAddr netAddr;
740 nsCOMPtr<nsINetAddr> nsAddr;
741 mMessage->GetFromAddr(getter_AddRefs(nsAddr));
742 nsAddr->GetNetAddr(&netAddr);
743
744 nsCOMPtr<nsIOutputStream> outputStream;
745 mMessage->GetOutputStream(getter_AddRefs(outputStream));
746
747 FallibleTArray<uint8_t>& data = mMessage->GetDataAsTArray();
748
749 nsCOMPtr<nsIUDPMessage> message = new nsUDPMessage(&netAddr,
750 outputStream,
751 data);
752 mListener->OnPacketReceived(mSocket, message);
753 return NS_OK;
754 }
755
756 NS_IMETHODIMP
757 SocketListenerProxy::OnStopListeningRunnable::Run()
758 {
759 mListener->OnStopListening(mSocket, mStatus);
760 return NS_OK;
761 }
762
763 class PendingSend : public nsIDNSListener
764 {
765 public:
766 NS_DECL_THREADSAFE_ISUPPORTS
767 NS_DECL_NSIDNSLISTENER
768
769 PendingSend(nsUDPSocket *aSocket, uint16_t aPort,
770 FallibleTArray<uint8_t> &aData)
771 : mSocket(aSocket)
772 , mPort(aPort)
773 {
774 mData.SwapElements(aData);
775 }
776
777 virtual ~PendingSend() {}
778
779 private:
780 nsRefPtr<nsUDPSocket> mSocket;
781 uint16_t mPort;
782 FallibleTArray<uint8_t> mData;
783 };
784
785 NS_IMPL_ISUPPORTS(PendingSend, nsIDNSListener)
786
787 NS_IMETHODIMP
788 PendingSend::OnLookupComplete(nsICancelable *request,
789 nsIDNSRecord *rec,
790 nsresult status)
791 {
792 if (NS_FAILED(status)) {
793 NS_WARNING("Failed to send UDP packet due to DNS lookup failure");
794 return NS_OK;
795 }
796
797 NetAddr addr;
798 if (NS_SUCCEEDED(rec->GetNextAddr(mPort, &addr))) {
799 uint32_t count;
800 nsresult rv = mSocket->SendWithAddress(&addr, mData.Elements(),
801 mData.Length(), &count);
802 NS_ENSURE_SUCCESS(rv, rv);
803 }
804
805 return NS_OK;
806 }
807
808 class SendRequestRunnable: public nsRunnable {
809 public:
810 SendRequestRunnable(nsUDPSocket *aSocket,
811 const NetAddr &aAddr,
812 FallibleTArray<uint8_t> &aData)
813 : mSocket(aSocket)
814 , mAddr(aAddr)
815 , mData(aData)
816 { }
817
818 NS_DECL_NSIRUNNABLE
819
820 private:
821 nsRefPtr<nsUDPSocket> mSocket;
822 const NetAddr mAddr;
823 FallibleTArray<uint8_t> mData;
824 };
825
826 NS_IMETHODIMP
827 SendRequestRunnable::Run()
828 {
829 uint32_t count;
830 mSocket->SendWithAddress(&mAddr, mData.Elements(),
831 mData.Length(), &count);
832 return NS_OK;
833 }
834
835 } // anonymous namespace
836
837 NS_IMETHODIMP
838 nsUDPSocket::AsyncListen(nsIUDPSocketListener *aListener)
839 {
840 // ensuring mFD implies ensuring mLock
841 NS_ENSURE_TRUE(mFD, NS_ERROR_NOT_INITIALIZED);
842 NS_ENSURE_TRUE(mListener == nullptr, NS_ERROR_IN_PROGRESS);
843 {
844 MutexAutoLock lock(mLock);
845 mListener = new SocketListenerProxy(aListener);
846 mListenerTarget = NS_GetCurrentThread();
847 }
848 return PostEvent(this, &nsUDPSocket::OnMsgAttach);
849 }
850
851 NS_IMETHODIMP
852 nsUDPSocket::Send(const nsACString &aHost, uint16_t aPort,
853 const uint8_t *aData, uint32_t aDataLength,
854 uint32_t *_retval)
855 {
856 NS_ENSURE_ARG(aData);
857 NS_ENSURE_ARG_POINTER(_retval);
858
859 *_retval = 0;
860
861 FallibleTArray<uint8_t> fallibleArray;
862 if (!fallibleArray.InsertElementsAt(0, aData, aDataLength)) {
863 return NS_ERROR_OUT_OF_MEMORY;
864 }
865
866 nsCOMPtr<nsIDNSListener> listener = new PendingSend(this, aPort, fallibleArray);
867
868 nsresult rv = ResolveHost(aHost, listener);
869 NS_ENSURE_SUCCESS(rv, rv);
870
871 *_retval = aDataLength;
872 return NS_OK;
873 }
874
875 NS_IMETHODIMP
876 nsUDPSocket::SendWithAddr(nsINetAddr *aAddr, const uint8_t *aData,
877 uint32_t aDataLength, uint32_t *_retval)
878 {
879 NS_ENSURE_ARG(aAddr);
880 NS_ENSURE_ARG(aData);
881 NS_ENSURE_ARG_POINTER(_retval);
882
883 NetAddr netAddr;
884 aAddr->GetNetAddr(&netAddr);
885 return SendWithAddress(&netAddr, aData, aDataLength, _retval);
886 }
887
888 NS_IMETHODIMP
889 nsUDPSocket::SendWithAddress(const NetAddr *aAddr, const uint8_t *aData,
890 uint32_t aDataLength, uint32_t *_retval)
891 {
892 NS_ENSURE_ARG(aAddr);
893 NS_ENSURE_ARG(aData);
894 NS_ENSURE_ARG_POINTER(_retval);
895
896 *_retval = 0;
897
898 PRNetAddr prAddr;
899 NetAddrToPRNetAddr(aAddr, &prAddr);
900
901 bool onSTSThread = false;
902 mSts->IsOnCurrentThread(&onSTSThread);
903
904 if (onSTSThread) {
905 MutexAutoLock lock(mLock);
906 if (!mFD) {
907 // socket is not initialized or has been closed
908 return NS_ERROR_FAILURE;
909 }
910 int32_t count = PR_SendTo(mFD, aData, sizeof(uint8_t) *aDataLength,
911 0, &prAddr, PR_INTERVAL_NO_WAIT);
912 if (count < 0) {
913 PRErrorCode code = PR_GetError();
914 return ErrorAccordingToNSPR(code);
915 }
916 this->AddOutputBytes(count);
917 *_retval = count;
918 } else {
919 FallibleTArray<uint8_t> fallibleArray;
920 if (!fallibleArray.InsertElementsAt(0, aData, aDataLength)) {
921 return NS_ERROR_OUT_OF_MEMORY;
922 }
923
924 nsresult rv = mSts->Dispatch(new SendRequestRunnable(this, *aAddr, fallibleArray),
925 NS_DISPATCH_NORMAL);
926 NS_ENSURE_SUCCESS(rv, rv);
927 *_retval = aDataLength;
928 }
929 return NS_OK;
930 }

mercurial