|
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 } |