1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/base/src/nsUDPSocket.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,930 @@ 1.4 +/* vim:set ts=2 sw=2 et cindent: */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "mozilla/Attributes.h" 1.10 +#include "mozilla/Endian.h" 1.11 +#include "mozilla/dom/TypedArray.h" 1.12 +#include "mozilla/HoldDropJSObjects.h" 1.13 + 1.14 +#include "nsSocketTransport2.h" 1.15 +#include "nsUDPSocket.h" 1.16 +#include "nsProxyRelease.h" 1.17 +#include "nsAutoPtr.h" 1.18 +#include "nsError.h" 1.19 +#include "nsNetCID.h" 1.20 +#include "prnetdb.h" 1.21 +#include "prio.h" 1.22 +#include "nsNetAddr.h" 1.23 +#include "nsNetSegmentUtils.h" 1.24 +#include "NetworkActivityMonitor.h" 1.25 +#include "nsStreamUtils.h" 1.26 +#include "nsIPipe.h" 1.27 +#include "prerror.h" 1.28 +#include "nsThreadUtils.h" 1.29 +#include "nsIDNSRecord.h" 1.30 +#include "nsIDNSService.h" 1.31 +#include "nsICancelable.h" 1.32 + 1.33 +using namespace mozilla::net; 1.34 +using namespace mozilla; 1.35 + 1.36 +static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID); 1.37 + 1.38 +//----------------------------------------------------------------------------- 1.39 + 1.40 +typedef void (nsUDPSocket:: *nsUDPSocketFunc)(void); 1.41 + 1.42 +static nsresult 1.43 +PostEvent(nsUDPSocket *s, nsUDPSocketFunc func) 1.44 +{ 1.45 + nsCOMPtr<nsIRunnable> ev = NS_NewRunnableMethod(s, func); 1.46 + 1.47 + if (!gSocketTransportService) 1.48 + return NS_ERROR_FAILURE; 1.49 + 1.50 + return gSocketTransportService->Dispatch(ev, NS_DISPATCH_NORMAL); 1.51 +} 1.52 + 1.53 +static nsresult 1.54 +ResolveHost(const nsACString &host, nsIDNSListener *listener) 1.55 +{ 1.56 + nsresult rv; 1.57 + 1.58 + nsCOMPtr<nsIDNSService> dns = 1.59 + do_GetService("@mozilla.org/network/dns-service;1", &rv); 1.60 + if (NS_FAILED(rv)) { 1.61 + return rv; 1.62 + } 1.63 + 1.64 + nsCOMPtr<nsICancelable> tmpOutstanding; 1.65 + return dns->AsyncResolve(host, 0, listener, nullptr, 1.66 + getter_AddRefs(tmpOutstanding)); 1.67 + 1.68 +} 1.69 + 1.70 +//----------------------------------------------------------------------------- 1.71 +// nsUDPOutputStream impl 1.72 +//----------------------------------------------------------------------------- 1.73 +NS_IMPL_ISUPPORTS(nsUDPOutputStream, nsIOutputStream) 1.74 + 1.75 +nsUDPOutputStream::nsUDPOutputStream(nsUDPSocket* aSocket, 1.76 + PRFileDesc* aFD, 1.77 + PRNetAddr& aPrClientAddr) 1.78 + : mSocket(aSocket) 1.79 + , mFD(aFD) 1.80 + , mPrClientAddr(aPrClientAddr) 1.81 + , mIsClosed(false) 1.82 +{ 1.83 +} 1.84 + 1.85 +nsUDPOutputStream::~nsUDPOutputStream() 1.86 +{ 1.87 +} 1.88 + 1.89 +/* void close (); */ 1.90 +NS_IMETHODIMP nsUDPOutputStream::Close() 1.91 +{ 1.92 + if (mIsClosed) 1.93 + return NS_BASE_STREAM_CLOSED; 1.94 + 1.95 + mIsClosed = true; 1.96 + return NS_OK; 1.97 +} 1.98 + 1.99 +/* void flush (); */ 1.100 +NS_IMETHODIMP nsUDPOutputStream::Flush() 1.101 +{ 1.102 + return NS_OK; 1.103 +} 1.104 + 1.105 +/* unsigned long write (in string aBuf, in unsigned long aCount); */ 1.106 +NS_IMETHODIMP nsUDPOutputStream::Write(const char * aBuf, uint32_t aCount, uint32_t *_retval) 1.107 +{ 1.108 + if (mIsClosed) 1.109 + return NS_BASE_STREAM_CLOSED; 1.110 + 1.111 + *_retval = 0; 1.112 + int32_t count = PR_SendTo(mFD, aBuf, aCount, 0, &mPrClientAddr, PR_INTERVAL_NO_WAIT); 1.113 + if (count < 0) { 1.114 + PRErrorCode code = PR_GetError(); 1.115 + return ErrorAccordingToNSPR(code); 1.116 + } 1.117 + 1.118 + *_retval = count; 1.119 + 1.120 + mSocket->AddOutputBytes(count); 1.121 + 1.122 + return NS_OK; 1.123 +} 1.124 + 1.125 +/* unsigned long writeFrom (in nsIInputStream aFromStream, in unsigned long aCount); */ 1.126 +NS_IMETHODIMP nsUDPOutputStream::WriteFrom(nsIInputStream *aFromStream, uint32_t aCount, uint32_t *_retval) 1.127 +{ 1.128 + return NS_ERROR_NOT_IMPLEMENTED; 1.129 +} 1.130 + 1.131 +/* [noscript] unsigned long writeSegments (in nsReadSegmentFun aReader, in voidPtr aClosure, in unsigned long aCount); */ 1.132 +NS_IMETHODIMP nsUDPOutputStream::WriteSegments(nsReadSegmentFun aReader, void *aClosure, uint32_t aCount, uint32_t *_retval) 1.133 +{ 1.134 + return NS_ERROR_NOT_IMPLEMENTED; 1.135 +} 1.136 + 1.137 +/* boolean isNonBlocking (); */ 1.138 +NS_IMETHODIMP nsUDPOutputStream::IsNonBlocking(bool *_retval) 1.139 +{ 1.140 + *_retval = true; 1.141 + return NS_OK; 1.142 +} 1.143 + 1.144 +//----------------------------------------------------------------------------- 1.145 +// nsUDPMessage impl 1.146 +//----------------------------------------------------------------------------- 1.147 +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsUDPMessage) 1.148 +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsUDPMessage) 1.149 + 1.150 +NS_IMPL_CYCLE_COLLECTION_CLASS(nsUDPMessage) 1.151 + 1.152 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsUDPMessage) 1.153 + NS_INTERFACE_MAP_ENTRY(nsISupports) 1.154 + NS_INTERFACE_MAP_ENTRY(nsIUDPMessage) 1.155 +NS_INTERFACE_MAP_END 1.156 + 1.157 +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsUDPMessage) 1.158 + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mJsobj) 1.159 +NS_IMPL_CYCLE_COLLECTION_TRACE_END 1.160 + 1.161 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsUDPMessage) 1.162 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS 1.163 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 1.164 + 1.165 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsUDPMessage) 1.166 + tmp->mJsobj = nullptr; 1.167 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END 1.168 + 1.169 +nsUDPMessage::nsUDPMessage(NetAddr* aAddr, 1.170 + nsIOutputStream* aOutputStream, 1.171 + FallibleTArray<uint8_t>& aData) 1.172 + : mOutputStream(aOutputStream) 1.173 +{ 1.174 + memcpy(&mAddr, aAddr, sizeof(NetAddr)); 1.175 + aData.SwapElements(mData); 1.176 +} 1.177 + 1.178 +nsUDPMessage::~nsUDPMessage() 1.179 +{ 1.180 + mozilla::DropJSObjects(this); 1.181 +} 1.182 + 1.183 +/* readonly attribute nsINetAddr from; */ 1.184 +NS_IMETHODIMP 1.185 +nsUDPMessage::GetFromAddr(nsINetAddr * *aFromAddr) 1.186 +{ 1.187 + NS_ENSURE_ARG_POINTER(aFromAddr); 1.188 + 1.189 + nsCOMPtr<nsINetAddr> result = new nsNetAddr(&mAddr); 1.190 + result.forget(aFromAddr); 1.191 + 1.192 + return NS_OK; 1.193 +} 1.194 + 1.195 +/* readonly attribute ACString data; */ 1.196 +NS_IMETHODIMP 1.197 +nsUDPMessage::GetData(nsACString & aData) 1.198 +{ 1.199 + aData.Assign(reinterpret_cast<const char*>(mData.Elements()), mData.Length()); 1.200 + return NS_OK; 1.201 +} 1.202 + 1.203 +/* readonly attribute nsIOutputStream outputStream; */ 1.204 +NS_IMETHODIMP 1.205 +nsUDPMessage::GetOutputStream(nsIOutputStream * *aOutputStream) 1.206 +{ 1.207 + NS_ENSURE_ARG_POINTER(aOutputStream); 1.208 + NS_IF_ADDREF(*aOutputStream = mOutputStream); 1.209 + return NS_OK; 1.210 +} 1.211 + 1.212 +/* readonly attribute jsval rawData; */ 1.213 +NS_IMETHODIMP 1.214 +nsUDPMessage::GetRawData(JSContext* cx, 1.215 + JS::MutableHandleValue aRawData) 1.216 +{ 1.217 + if(!mJsobj){ 1.218 + mJsobj = mozilla::dom::Uint8Array::Create(cx, nullptr, mData.Length(), mData.Elements()); 1.219 + mozilla::HoldJSObjects(this); 1.220 + } 1.221 + aRawData.setObject(*mJsobj); 1.222 + return NS_OK; 1.223 +} 1.224 + 1.225 +/* [noscript, notxpcom, nostdcall] Uint8ArrayRef getDataAsTArray(); */ 1.226 +FallibleTArray<uint8_t>& 1.227 +nsUDPMessage::GetDataAsTArray() 1.228 +{ 1.229 + return mData; 1.230 +} 1.231 + 1.232 +//----------------------------------------------------------------------------- 1.233 +// nsUDPSocket 1.234 +//----------------------------------------------------------------------------- 1.235 + 1.236 +nsUDPSocket::nsUDPSocket() 1.237 + : mLock("nsUDPSocket.mLock") 1.238 + , mFD(nullptr) 1.239 + , mAttached(false) 1.240 + , mByteReadCount(0) 1.241 + , mByteWriteCount(0) 1.242 +{ 1.243 + mAddr.raw.family = PR_AF_UNSPEC; 1.244 + // we want to be able to access the STS directly, and it may not have been 1.245 + // constructed yet. the STS constructor sets gSocketTransportService. 1.246 + if (!gSocketTransportService) 1.247 + { 1.248 + // This call can fail if we're offline, for example. 1.249 + nsCOMPtr<nsISocketTransportService> sts = 1.250 + do_GetService(kSocketTransportServiceCID); 1.251 + } 1.252 + 1.253 + mSts = gSocketTransportService; 1.254 + MOZ_COUNT_CTOR(nsUDPSocket); 1.255 +} 1.256 + 1.257 +nsUDPSocket::~nsUDPSocket() 1.258 +{ 1.259 + Close(); // just in case :) 1.260 + 1.261 + MOZ_COUNT_DTOR(nsUDPSocket); 1.262 +} 1.263 + 1.264 +void 1.265 +nsUDPSocket::AddOutputBytes(uint64_t aBytes) 1.266 +{ 1.267 + mByteWriteCount += aBytes; 1.268 +} 1.269 + 1.270 +void 1.271 +nsUDPSocket::OnMsgClose() 1.272 +{ 1.273 + SOCKET_LOG(("nsUDPSocket::OnMsgClose [this=%p]\n", this)); 1.274 + 1.275 + if (NS_FAILED(mCondition)) 1.276 + return; 1.277 + 1.278 + // tear down socket. this signals the STS to detach our socket handler. 1.279 + mCondition = NS_BINDING_ABORTED; 1.280 + 1.281 + // if we are attached, then socket transport service will call our 1.282 + // OnSocketDetached method automatically. Otherwise, we have to call it 1.283 + // (and thus close the socket) manually. 1.284 + if (!mAttached) 1.285 + OnSocketDetached(mFD); 1.286 +} 1.287 + 1.288 +void 1.289 +nsUDPSocket::OnMsgAttach() 1.290 +{ 1.291 + SOCKET_LOG(("nsUDPSocket::OnMsgAttach [this=%p]\n", this)); 1.292 + 1.293 + if (NS_FAILED(mCondition)) 1.294 + return; 1.295 + 1.296 + mCondition = TryAttach(); 1.297 + 1.298 + // if we hit an error while trying to attach then bail... 1.299 + if (NS_FAILED(mCondition)) 1.300 + { 1.301 + NS_ASSERTION(!mAttached, "should not be attached already"); 1.302 + OnSocketDetached(mFD); 1.303 + } 1.304 +} 1.305 + 1.306 +nsresult 1.307 +nsUDPSocket::TryAttach() 1.308 +{ 1.309 + nsresult rv; 1.310 + 1.311 + if (!gSocketTransportService) 1.312 + return NS_ERROR_FAILURE; 1.313 + 1.314 + // 1.315 + // find out if it is going to be ok to attach another socket to the STS. 1.316 + // if not then we have to wait for the STS to tell us that it is ok. 1.317 + // the notification is asynchronous, which means that when we could be 1.318 + // in a race to call AttachSocket once notified. for this reason, when 1.319 + // we get notified, we just re-enter this function. as a result, we are 1.320 + // sure to ask again before calling AttachSocket. in this way we deal 1.321 + // with the race condition. though it isn't the most elegant solution, 1.322 + // it is far simpler than trying to build a system that would guarantee 1.323 + // FIFO ordering (which wouldn't even be that valuable IMO). see bug 1.324 + // 194402 for more info. 1.325 + // 1.326 + if (!gSocketTransportService->CanAttachSocket()) 1.327 + { 1.328 + nsCOMPtr<nsIRunnable> event = 1.329 + NS_NewRunnableMethod(this, &nsUDPSocket::OnMsgAttach); 1.330 + 1.331 + nsresult rv = gSocketTransportService->NotifyWhenCanAttachSocket(event); 1.332 + if (NS_FAILED(rv)) 1.333 + return rv; 1.334 + } 1.335 + 1.336 + // 1.337 + // ok, we can now attach our socket to the STS for polling 1.338 + // 1.339 + rv = gSocketTransportService->AttachSocket(mFD, this); 1.340 + if (NS_FAILED(rv)) 1.341 + return rv; 1.342 + 1.343 + mAttached = true; 1.344 + 1.345 + // 1.346 + // now, configure our poll flags for listening... 1.347 + // 1.348 + mPollFlags = (PR_POLL_READ | PR_POLL_EXCEPT); 1.349 + return NS_OK; 1.350 +} 1.351 + 1.352 +namespace { 1.353 +//----------------------------------------------------------------------------- 1.354 +// UDPMessageProxy 1.355 +//----------------------------------------------------------------------------- 1.356 +class UDPMessageProxy MOZ_FINAL : public nsIUDPMessage 1.357 +{ 1.358 +public: 1.359 + UDPMessageProxy(NetAddr* aAddr, 1.360 + nsIOutputStream* aOutputStream, 1.361 + FallibleTArray<uint8_t>& aData) 1.362 + : mOutputStream(aOutputStream) 1.363 + { 1.364 + memcpy(&mAddr, aAddr, sizeof(NetAddr)); 1.365 + aData.SwapElements(mData); 1.366 + } 1.367 + 1.368 + NS_DECL_THREADSAFE_ISUPPORTS 1.369 + NS_DECL_NSIUDPMESSAGE 1.370 + 1.371 +private: 1.372 + NetAddr mAddr; 1.373 + nsCOMPtr<nsIOutputStream> mOutputStream; 1.374 + FallibleTArray<uint8_t> mData; 1.375 +}; 1.376 + 1.377 +NS_IMPL_ISUPPORTS(UDPMessageProxy, nsIUDPMessage) 1.378 + 1.379 +/* readonly attribute nsINetAddr from; */ 1.380 +NS_IMETHODIMP 1.381 +UDPMessageProxy::GetFromAddr(nsINetAddr * *aFromAddr) 1.382 +{ 1.383 + NS_ENSURE_ARG_POINTER(aFromAddr); 1.384 + 1.385 + nsCOMPtr<nsINetAddr> result = new nsNetAddr(&mAddr); 1.386 + result.forget(aFromAddr); 1.387 + 1.388 + return NS_OK; 1.389 +} 1.390 + 1.391 +/* readonly attribute ACString data; */ 1.392 +NS_IMETHODIMP 1.393 +UDPMessageProxy::GetData(nsACString & aData) 1.394 +{ 1.395 + return NS_ERROR_NOT_IMPLEMENTED; 1.396 +} 1.397 + 1.398 +/* [noscript, notxpcom, nostdcall] Uint8TArrayRef getDataAsTArray(); */ 1.399 +FallibleTArray<uint8_t>& 1.400 +UDPMessageProxy::GetDataAsTArray() 1.401 +{ 1.402 + return mData; 1.403 +} 1.404 + 1.405 +/* readonly attribute jsval rawData; */ 1.406 +NS_IMETHODIMP 1.407 +UDPMessageProxy::GetRawData(JSContext* cx, 1.408 + JS::MutableHandleValue aRawData) 1.409 +{ 1.410 + return NS_ERROR_NOT_IMPLEMENTED; 1.411 +} 1.412 + 1.413 +/* readonly attribute nsIOutputStream outputStream; */ 1.414 +NS_IMETHODIMP 1.415 +UDPMessageProxy::GetOutputStream(nsIOutputStream * *aOutputStream) 1.416 +{ 1.417 + NS_ENSURE_ARG_POINTER(aOutputStream); 1.418 + NS_IF_ADDREF(*aOutputStream = mOutputStream); 1.419 + return NS_OK; 1.420 +} 1.421 + 1.422 +} //anonymous namespace 1.423 + 1.424 +//----------------------------------------------------------------------------- 1.425 +// nsUDPSocket::nsASocketHandler 1.426 +//----------------------------------------------------------------------------- 1.427 + 1.428 +void 1.429 +nsUDPSocket::OnSocketReady(PRFileDesc *fd, int16_t outFlags) 1.430 +{ 1.431 + NS_ASSERTION(NS_SUCCEEDED(mCondition), "oops"); 1.432 + NS_ASSERTION(mFD == fd, "wrong file descriptor"); 1.433 + NS_ASSERTION(outFlags != -1, "unexpected timeout condition reached"); 1.434 + 1.435 + if (outFlags & (PR_POLL_ERR | PR_POLL_HUP | PR_POLL_NVAL)) 1.436 + { 1.437 + NS_WARNING("error polling on listening socket"); 1.438 + mCondition = NS_ERROR_UNEXPECTED; 1.439 + return; 1.440 + } 1.441 + 1.442 + PRNetAddr prClientAddr; 1.443 + uint32_t count; 1.444 + char buff[1500]; 1.445 + count = PR_RecvFrom(mFD, buff, sizeof(buff), 0, &prClientAddr, PR_INTERVAL_NO_WAIT); 1.446 + 1.447 + if (count < 1) { 1.448 + NS_WARNING("error of recvfrom on UDP socket"); 1.449 + mCondition = NS_ERROR_UNEXPECTED; 1.450 + return; 1.451 + } 1.452 + mByteReadCount += count; 1.453 + 1.454 + FallibleTArray<uint8_t> data; 1.455 + if(!data.AppendElements(buff, count)){ 1.456 + mCondition = NS_ERROR_UNEXPECTED; 1.457 + return; 1.458 + } 1.459 + 1.460 + nsCOMPtr<nsIAsyncInputStream> pipeIn; 1.461 + nsCOMPtr<nsIAsyncOutputStream> pipeOut; 1.462 + 1.463 + uint32_t segsize = 1400; 1.464 + uint32_t segcount = 0; 1.465 + net_ResolveSegmentParams(segsize, segcount); 1.466 + nsresult rv = NS_NewPipe2(getter_AddRefs(pipeIn), getter_AddRefs(pipeOut), 1.467 + true, true, segsize, segcount); 1.468 + 1.469 + if (NS_FAILED(rv)) { 1.470 + return; 1.471 + } 1.472 + 1.473 + nsRefPtr<nsUDPOutputStream> os = new nsUDPOutputStream(this, mFD, prClientAddr); 1.474 + rv = NS_AsyncCopy(pipeIn, os, mSts, 1.475 + NS_ASYNCCOPY_VIA_READSEGMENTS, 1400); 1.476 + 1.477 + if (NS_FAILED(rv)) { 1.478 + return; 1.479 + } 1.480 + 1.481 + NetAddr netAddr; 1.482 + PRNetAddrToNetAddr(&prClientAddr, &netAddr); 1.483 + nsCOMPtr<nsIUDPMessage> message = new UDPMessageProxy(&netAddr, pipeOut, data); 1.484 + mListener->OnPacketReceived(this, message); 1.485 +} 1.486 + 1.487 +void 1.488 +nsUDPSocket::OnSocketDetached(PRFileDesc *fd) 1.489 +{ 1.490 + // force a failure condition if none set; maybe the STS is shutting down :-/ 1.491 + if (NS_SUCCEEDED(mCondition)) 1.492 + mCondition = NS_ERROR_ABORT; 1.493 + 1.494 + if (mFD) 1.495 + { 1.496 + NS_ASSERTION(mFD == fd, "wrong file descriptor"); 1.497 + PR_Close(mFD); 1.498 + mFD = nullptr; 1.499 + } 1.500 + 1.501 + if (mListener) 1.502 + { 1.503 + // need to atomically clear mListener. see our Close() method. 1.504 + nsCOMPtr<nsIUDPSocketListener> listener; 1.505 + { 1.506 + MutexAutoLock lock(mLock); 1.507 + mListener.swap(listener); 1.508 + } 1.509 + 1.510 + if (listener) { 1.511 + listener->OnStopListening(this, mCondition); 1.512 + NS_ProxyRelease(mListenerTarget, listener); 1.513 + } 1.514 + } 1.515 +} 1.516 + 1.517 +void 1.518 +nsUDPSocket::IsLocal(bool *aIsLocal) 1.519 +{ 1.520 + // If bound to loopback, this UDP socket only accepts local connections. 1.521 + *aIsLocal = mAddr.raw.family == nsINetAddr::FAMILY_LOCAL; 1.522 +} 1.523 + 1.524 +//----------------------------------------------------------------------------- 1.525 +// nsSocket::nsISupports 1.526 +//----------------------------------------------------------------------------- 1.527 + 1.528 +NS_IMPL_ISUPPORTS(nsUDPSocket, nsIUDPSocket) 1.529 + 1.530 + 1.531 +//----------------------------------------------------------------------------- 1.532 +// nsSocket::nsISocket 1.533 +//----------------------------------------------------------------------------- 1.534 + 1.535 +NS_IMETHODIMP 1.536 +nsUDPSocket::Init(int32_t aPort, bool aLoopbackOnly) 1.537 +{ 1.538 + NetAddr addr; 1.539 + 1.540 + if (aPort < 0) 1.541 + aPort = 0; 1.542 + 1.543 + addr.raw.family = AF_INET; 1.544 + addr.inet.port = htons(aPort); 1.545 + 1.546 + if (aLoopbackOnly) 1.547 + addr.inet.ip = htonl(INADDR_LOOPBACK); 1.548 + else 1.549 + addr.inet.ip = htonl(INADDR_ANY); 1.550 + 1.551 + return InitWithAddress(&addr); 1.552 +} 1.553 + 1.554 +NS_IMETHODIMP 1.555 +nsUDPSocket::InitWithAddress(const NetAddr *aAddr) 1.556 +{ 1.557 + NS_ENSURE_TRUE(mFD == nullptr, NS_ERROR_ALREADY_INITIALIZED); 1.558 + 1.559 + // 1.560 + // configure listening socket... 1.561 + // 1.562 + 1.563 + mFD = PR_OpenUDPSocket(aAddr->raw.family); 1.564 + if (!mFD) 1.565 + { 1.566 + NS_WARNING("unable to create UDP socket"); 1.567 + return NS_ERROR_FAILURE; 1.568 + } 1.569 + 1.570 + uint16_t port; 1.571 + if (NS_FAILED(net::GetPort(aAddr, &port))) { 1.572 + NS_WARNING("invalid bind address"); 1.573 + goto fail; 1.574 + } 1.575 + 1.576 + PRSocketOptionData opt; 1.577 + 1.578 + // Linux kernel will sometimes hand out a used port if we bind 1.579 + // to port 0 with SO_REUSEADDR 1.580 + if (port) { 1.581 + opt.option = PR_SockOpt_Reuseaddr; 1.582 + opt.value.reuse_addr = true; 1.583 + PR_SetSocketOption(mFD, &opt); 1.584 + } 1.585 + 1.586 + opt.option = PR_SockOpt_Nonblocking; 1.587 + opt.value.non_blocking = true; 1.588 + PR_SetSocketOption(mFD, &opt); 1.589 + 1.590 + PRNetAddr addr; 1.591 + PR_InitializeNetAddr(PR_IpAddrAny, 0, &addr); 1.592 + NetAddrToPRNetAddr(aAddr, &addr); 1.593 + 1.594 + if (PR_Bind(mFD, &addr) != PR_SUCCESS) 1.595 + { 1.596 + NS_WARNING("failed to bind socket"); 1.597 + goto fail; 1.598 + } 1.599 + 1.600 + // get the resulting socket address, which may be different than what 1.601 + // we passed to bind. 1.602 + if (PR_GetSockName(mFD, &addr) != PR_SUCCESS) 1.603 + { 1.604 + NS_WARNING("cannot get socket name"); 1.605 + goto fail; 1.606 + } 1.607 + 1.608 + PRNetAddrToNetAddr(&addr, &mAddr); 1.609 + 1.610 + // create proxy via NetworkActivityMonitor 1.611 + NetworkActivityMonitor::AttachIOLayer(mFD); 1.612 + 1.613 + // wait until AsyncListen is called before polling the socket for 1.614 + // client connections. 1.615 + return NS_OK; 1.616 + 1.617 +fail: 1.618 + Close(); 1.619 + return NS_ERROR_FAILURE; 1.620 +} 1.621 + 1.622 +NS_IMETHODIMP 1.623 +nsUDPSocket::Close() 1.624 +{ 1.625 + { 1.626 + MutexAutoLock lock(mLock); 1.627 + // we want to proxy the close operation to the socket thread if a listener 1.628 + // has been set. otherwise, we should just close the socket here... 1.629 + if (!mListener) 1.630 + { 1.631 + if (mFD) 1.632 + { 1.633 + PR_Close(mFD); 1.634 + mFD = nullptr; 1.635 + } 1.636 + return NS_OK; 1.637 + } 1.638 + } 1.639 + return PostEvent(this, &nsUDPSocket::OnMsgClose); 1.640 +} 1.641 + 1.642 +NS_IMETHODIMP 1.643 +nsUDPSocket::GetPort(int32_t *aResult) 1.644 +{ 1.645 + // no need to enter the lock here 1.646 + uint16_t result; 1.647 + nsresult rv = net::GetPort(&mAddr, &result); 1.648 + *aResult = static_cast<int32_t>(result); 1.649 + return rv; 1.650 +} 1.651 + 1.652 +NS_IMETHODIMP 1.653 +nsUDPSocket::GetAddress(NetAddr *aResult) 1.654 +{ 1.655 + // no need to enter the lock here 1.656 + memcpy(aResult, &mAddr, sizeof(mAddr)); 1.657 + return NS_OK; 1.658 +} 1.659 + 1.660 +namespace { 1.661 +//----------------------------------------------------------------------------- 1.662 +// SocketListenerProxy 1.663 +//----------------------------------------------------------------------------- 1.664 +class SocketListenerProxy MOZ_FINAL : public nsIUDPSocketListener 1.665 +{ 1.666 +public: 1.667 + SocketListenerProxy(nsIUDPSocketListener* aListener) 1.668 + : mListener(new nsMainThreadPtrHolder<nsIUDPSocketListener>(aListener)) 1.669 + , mTargetThread(do_GetCurrentThread()) 1.670 + { } 1.671 + 1.672 + NS_DECL_THREADSAFE_ISUPPORTS 1.673 + NS_DECL_NSIUDPSOCKETLISTENER 1.674 + 1.675 + class OnPacketReceivedRunnable : public nsRunnable 1.676 + { 1.677 + public: 1.678 + OnPacketReceivedRunnable(const nsMainThreadPtrHandle<nsIUDPSocketListener>& aListener, 1.679 + nsIUDPSocket* aSocket, 1.680 + nsIUDPMessage* aMessage) 1.681 + : mListener(aListener) 1.682 + , mSocket(aSocket) 1.683 + , mMessage(aMessage) 1.684 + { } 1.685 + 1.686 + NS_DECL_NSIRUNNABLE 1.687 + 1.688 + private: 1.689 + nsMainThreadPtrHandle<nsIUDPSocketListener> mListener; 1.690 + nsCOMPtr<nsIUDPSocket> mSocket; 1.691 + nsCOMPtr<nsIUDPMessage> mMessage; 1.692 + }; 1.693 + 1.694 + class OnStopListeningRunnable : public nsRunnable 1.695 + { 1.696 + public: 1.697 + OnStopListeningRunnable(const nsMainThreadPtrHandle<nsIUDPSocketListener>& aListener, 1.698 + nsIUDPSocket* aSocket, 1.699 + nsresult aStatus) 1.700 + : mListener(aListener) 1.701 + , mSocket(aSocket) 1.702 + , mStatus(aStatus) 1.703 + { } 1.704 + 1.705 + NS_DECL_NSIRUNNABLE 1.706 + 1.707 + private: 1.708 + nsMainThreadPtrHandle<nsIUDPSocketListener> mListener; 1.709 + nsCOMPtr<nsIUDPSocket> mSocket; 1.710 + nsresult mStatus; 1.711 + }; 1.712 + 1.713 +private: 1.714 + nsMainThreadPtrHandle<nsIUDPSocketListener> mListener; 1.715 + nsCOMPtr<nsIEventTarget> mTargetThread; 1.716 +}; 1.717 + 1.718 +NS_IMPL_ISUPPORTS(SocketListenerProxy, 1.719 + nsIUDPSocketListener) 1.720 + 1.721 +NS_IMETHODIMP 1.722 +SocketListenerProxy::OnPacketReceived(nsIUDPSocket* aSocket, 1.723 + nsIUDPMessage* aMessage) 1.724 +{ 1.725 + nsRefPtr<OnPacketReceivedRunnable> r = 1.726 + new OnPacketReceivedRunnable(mListener, aSocket, aMessage); 1.727 + return mTargetThread->Dispatch(r, NS_DISPATCH_NORMAL); 1.728 +} 1.729 + 1.730 +NS_IMETHODIMP 1.731 +SocketListenerProxy::OnStopListening(nsIUDPSocket* aSocket, 1.732 + nsresult aStatus) 1.733 +{ 1.734 + nsRefPtr<OnStopListeningRunnable> r = 1.735 + new OnStopListeningRunnable(mListener, aSocket, aStatus); 1.736 + return mTargetThread->Dispatch(r, NS_DISPATCH_NORMAL); 1.737 +} 1.738 + 1.739 +NS_IMETHODIMP 1.740 +SocketListenerProxy::OnPacketReceivedRunnable::Run() 1.741 +{ 1.742 + NetAddr netAddr; 1.743 + nsCOMPtr<nsINetAddr> nsAddr; 1.744 + mMessage->GetFromAddr(getter_AddRefs(nsAddr)); 1.745 + nsAddr->GetNetAddr(&netAddr); 1.746 + 1.747 + nsCOMPtr<nsIOutputStream> outputStream; 1.748 + mMessage->GetOutputStream(getter_AddRefs(outputStream)); 1.749 + 1.750 + FallibleTArray<uint8_t>& data = mMessage->GetDataAsTArray(); 1.751 + 1.752 + nsCOMPtr<nsIUDPMessage> message = new nsUDPMessage(&netAddr, 1.753 + outputStream, 1.754 + data); 1.755 + mListener->OnPacketReceived(mSocket, message); 1.756 + return NS_OK; 1.757 +} 1.758 + 1.759 +NS_IMETHODIMP 1.760 +SocketListenerProxy::OnStopListeningRunnable::Run() 1.761 +{ 1.762 + mListener->OnStopListening(mSocket, mStatus); 1.763 + return NS_OK; 1.764 +} 1.765 + 1.766 +class PendingSend : public nsIDNSListener 1.767 +{ 1.768 +public: 1.769 + NS_DECL_THREADSAFE_ISUPPORTS 1.770 + NS_DECL_NSIDNSLISTENER 1.771 + 1.772 + PendingSend(nsUDPSocket *aSocket, uint16_t aPort, 1.773 + FallibleTArray<uint8_t> &aData) 1.774 + : mSocket(aSocket) 1.775 + , mPort(aPort) 1.776 + { 1.777 + mData.SwapElements(aData); 1.778 + } 1.779 + 1.780 + virtual ~PendingSend() {} 1.781 + 1.782 +private: 1.783 + nsRefPtr<nsUDPSocket> mSocket; 1.784 + uint16_t mPort; 1.785 + FallibleTArray<uint8_t> mData; 1.786 +}; 1.787 + 1.788 +NS_IMPL_ISUPPORTS(PendingSend, nsIDNSListener) 1.789 + 1.790 +NS_IMETHODIMP 1.791 +PendingSend::OnLookupComplete(nsICancelable *request, 1.792 + nsIDNSRecord *rec, 1.793 + nsresult status) 1.794 +{ 1.795 + if (NS_FAILED(status)) { 1.796 + NS_WARNING("Failed to send UDP packet due to DNS lookup failure"); 1.797 + return NS_OK; 1.798 + } 1.799 + 1.800 + NetAddr addr; 1.801 + if (NS_SUCCEEDED(rec->GetNextAddr(mPort, &addr))) { 1.802 + uint32_t count; 1.803 + nsresult rv = mSocket->SendWithAddress(&addr, mData.Elements(), 1.804 + mData.Length(), &count); 1.805 + NS_ENSURE_SUCCESS(rv, rv); 1.806 + } 1.807 + 1.808 + return NS_OK; 1.809 +} 1.810 + 1.811 +class SendRequestRunnable: public nsRunnable { 1.812 +public: 1.813 + SendRequestRunnable(nsUDPSocket *aSocket, 1.814 + const NetAddr &aAddr, 1.815 + FallibleTArray<uint8_t> &aData) 1.816 + : mSocket(aSocket) 1.817 + , mAddr(aAddr) 1.818 + , mData(aData) 1.819 + { } 1.820 + 1.821 + NS_DECL_NSIRUNNABLE 1.822 + 1.823 +private: 1.824 + nsRefPtr<nsUDPSocket> mSocket; 1.825 + const NetAddr mAddr; 1.826 + FallibleTArray<uint8_t> mData; 1.827 +}; 1.828 + 1.829 +NS_IMETHODIMP 1.830 +SendRequestRunnable::Run() 1.831 +{ 1.832 + uint32_t count; 1.833 + mSocket->SendWithAddress(&mAddr, mData.Elements(), 1.834 + mData.Length(), &count); 1.835 + return NS_OK; 1.836 +} 1.837 + 1.838 +} // anonymous namespace 1.839 + 1.840 +NS_IMETHODIMP 1.841 +nsUDPSocket::AsyncListen(nsIUDPSocketListener *aListener) 1.842 +{ 1.843 + // ensuring mFD implies ensuring mLock 1.844 + NS_ENSURE_TRUE(mFD, NS_ERROR_NOT_INITIALIZED); 1.845 + NS_ENSURE_TRUE(mListener == nullptr, NS_ERROR_IN_PROGRESS); 1.846 + { 1.847 + MutexAutoLock lock(mLock); 1.848 + mListener = new SocketListenerProxy(aListener); 1.849 + mListenerTarget = NS_GetCurrentThread(); 1.850 + } 1.851 + return PostEvent(this, &nsUDPSocket::OnMsgAttach); 1.852 +} 1.853 + 1.854 +NS_IMETHODIMP 1.855 +nsUDPSocket::Send(const nsACString &aHost, uint16_t aPort, 1.856 + const uint8_t *aData, uint32_t aDataLength, 1.857 + uint32_t *_retval) 1.858 +{ 1.859 + NS_ENSURE_ARG(aData); 1.860 + NS_ENSURE_ARG_POINTER(_retval); 1.861 + 1.862 + *_retval = 0; 1.863 + 1.864 + FallibleTArray<uint8_t> fallibleArray; 1.865 + if (!fallibleArray.InsertElementsAt(0, aData, aDataLength)) { 1.866 + return NS_ERROR_OUT_OF_MEMORY; 1.867 + } 1.868 + 1.869 + nsCOMPtr<nsIDNSListener> listener = new PendingSend(this, aPort, fallibleArray); 1.870 + 1.871 + nsresult rv = ResolveHost(aHost, listener); 1.872 + NS_ENSURE_SUCCESS(rv, rv); 1.873 + 1.874 + *_retval = aDataLength; 1.875 + return NS_OK; 1.876 +} 1.877 + 1.878 +NS_IMETHODIMP 1.879 +nsUDPSocket::SendWithAddr(nsINetAddr *aAddr, const uint8_t *aData, 1.880 + uint32_t aDataLength, uint32_t *_retval) 1.881 +{ 1.882 + NS_ENSURE_ARG(aAddr); 1.883 + NS_ENSURE_ARG(aData); 1.884 + NS_ENSURE_ARG_POINTER(_retval); 1.885 + 1.886 + NetAddr netAddr; 1.887 + aAddr->GetNetAddr(&netAddr); 1.888 + return SendWithAddress(&netAddr, aData, aDataLength, _retval); 1.889 +} 1.890 + 1.891 +NS_IMETHODIMP 1.892 +nsUDPSocket::SendWithAddress(const NetAddr *aAddr, const uint8_t *aData, 1.893 + uint32_t aDataLength, uint32_t *_retval) 1.894 +{ 1.895 + NS_ENSURE_ARG(aAddr); 1.896 + NS_ENSURE_ARG(aData); 1.897 + NS_ENSURE_ARG_POINTER(_retval); 1.898 + 1.899 + *_retval = 0; 1.900 + 1.901 + PRNetAddr prAddr; 1.902 + NetAddrToPRNetAddr(aAddr, &prAddr); 1.903 + 1.904 + bool onSTSThread = false; 1.905 + mSts->IsOnCurrentThread(&onSTSThread); 1.906 + 1.907 + if (onSTSThread) { 1.908 + MutexAutoLock lock(mLock); 1.909 + if (!mFD) { 1.910 + // socket is not initialized or has been closed 1.911 + return NS_ERROR_FAILURE; 1.912 + } 1.913 + int32_t count = PR_SendTo(mFD, aData, sizeof(uint8_t) *aDataLength, 1.914 + 0, &prAddr, PR_INTERVAL_NO_WAIT); 1.915 + if (count < 0) { 1.916 + PRErrorCode code = PR_GetError(); 1.917 + return ErrorAccordingToNSPR(code); 1.918 + } 1.919 + this->AddOutputBytes(count); 1.920 + *_retval = count; 1.921 + } else { 1.922 + FallibleTArray<uint8_t> fallibleArray; 1.923 + if (!fallibleArray.InsertElementsAt(0, aData, aDataLength)) { 1.924 + return NS_ERROR_OUT_OF_MEMORY; 1.925 + } 1.926 + 1.927 + nsresult rv = mSts->Dispatch(new SendRequestRunnable(this, *aAddr, fallibleArray), 1.928 + NS_DISPATCH_NORMAL); 1.929 + NS_ENSURE_SUCCESS(rv, rv); 1.930 + *_retval = aDataLength; 1.931 + } 1.932 + return NS_OK; 1.933 +}