netwerk/base/src/nsUDPSocket.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial