UDPSocketParent.cpp (15976B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "UDPSocketParent.h" 8 9 #include "UDPSocket.h" 10 #include "mozilla/dom/ContentParent.h" 11 #include "mozilla/ipc/InputStreamUtils.h" 12 #include "mozilla/ipc/PBackgroundParent.h" 13 #include "mozilla/net/DNS.h" 14 #include "mozilla/net/NeckoCommon.h" 15 #include "mozilla/net/PNeckoParent.h" 16 #include "nsComponentManagerUtils.h" 17 #include "nsINetAddr.h" 18 #include "nsIPermissionManager.h" 19 #include "nsIUDPSocket.h" 20 #include "nsNetCID.h" 21 #include "transport/runnable_utils.h" 22 23 namespace mozilla { 24 25 using namespace net; 26 27 namespace dom { 28 29 NS_IMPL_ISUPPORTS(UDPSocketParent, nsIUDPSocketListener) 30 31 UDPSocketParent::UDPSocketParent(PBackgroundParent* aManager) 32 : mBackgroundManager(aManager), mIPCOpen(true) {} 33 34 UDPSocketParent::UDPSocketParent(PNeckoParent* aManager) 35 : mBackgroundManager(nullptr), mIPCOpen(true) {} 36 37 UDPSocketParent::~UDPSocketParent() = default; 38 39 bool UDPSocketParent::Init(nsIPrincipal* aPrincipal, 40 const nsACString& aFilter) { 41 MOZ_ASSERT_IF(mBackgroundManager, !aPrincipal); 42 // will be used once we move all UDPSocket to PBackground, or 43 // if we add in Principal checking for dom/media/webrtc/transport 44 (void)mBackgroundManager; 45 46 mPrincipal = aPrincipal; 47 48 if (!aFilter.IsEmpty()) { 49 nsAutoCString contractId(NS_NETWORK_UDP_SOCKET_FILTER_HANDLER_PREFIX); 50 contractId.Append(aFilter); 51 nsCOMPtr<nsISocketFilterHandler> filterHandler = 52 do_GetService(contractId.get()); 53 if (filterHandler) { 54 nsresult rv = filterHandler->NewFilter(getter_AddRefs(mFilter)); 55 if (NS_FAILED(rv)) { 56 printf_stderr( 57 "Cannot create filter that content specified. " 58 "filter name: %s, error code: %u.", 59 aFilter.BeginReading(), static_cast<uint32_t>(rv)); 60 return false; 61 } 62 } else { 63 printf_stderr( 64 "Content doesn't have a valid filter. " 65 "filter name: %s.", 66 aFilter.BeginReading()); 67 return false; 68 } 69 } 70 71 return true; 72 } 73 74 // PUDPSocketParent methods 75 76 mozilla::ipc::IPCResult UDPSocketParent::RecvBind( 77 const UDPAddressInfo& aAddressInfo, const bool& aAddressReuse, 78 const bool& aLoopback, const uint32_t& recvBufferSize, 79 const uint32_t& sendBufferSize) { 80 UDPSOCKET_LOG(("%s: %s:%u", __FUNCTION__, aAddressInfo.addr().get(), 81 aAddressInfo.port())); 82 83 if (NS_FAILED(BindInternal(aAddressInfo.addr(), aAddressInfo.port(), 84 aAddressReuse, aLoopback, recvBufferSize, 85 sendBufferSize))) { 86 FireInternalError(__LINE__); 87 return IPC_OK(); 88 } 89 90 nsCOMPtr<nsINetAddr> localAddr; 91 mSocket->GetLocalAddr(getter_AddRefs(localAddr)); 92 93 nsCString addr; 94 if (NS_FAILED(localAddr->GetAddress(addr))) { 95 FireInternalError(__LINE__); 96 return IPC_OK(); 97 } 98 99 uint16_t port; 100 if (NS_FAILED(localAddr->GetPort(&port))) { 101 FireInternalError(__LINE__); 102 return IPC_OK(); 103 } 104 105 UDPSOCKET_LOG( 106 ("%s: SendCallbackOpened: %s:%u", __FUNCTION__, addr.get(), port)); 107 mAddress = {addr, port}; 108 (void)SendCallbackOpened(UDPAddressInfo(addr, port)); 109 110 return IPC_OK(); 111 } 112 113 nsresult UDPSocketParent::BindInternal(const nsCString& aHost, 114 const uint16_t& aPort, 115 const bool& aAddressReuse, 116 const bool& aLoopback, 117 const uint32_t& recvBufferSize, 118 const uint32_t& sendBufferSize) { 119 nsresult rv; 120 121 UDPSOCKET_LOG( 122 ("%s: [this=%p] %s:%u addressReuse: %d loopback: %d recvBufferSize: " 123 "%" PRIu32 ", sendBufferSize: %" PRIu32, 124 __FUNCTION__, this, nsCString(aHost).get(), aPort, aAddressReuse, 125 aLoopback, recvBufferSize, sendBufferSize)); 126 127 nsCOMPtr<nsIUDPSocket> sock = 128 do_CreateInstance("@mozilla.org/network/udp-socket;1", &rv); 129 130 if (NS_WARN_IF(NS_FAILED(rv))) { 131 return rv; 132 } 133 134 if (aHost.IsEmpty()) { 135 rv = sock->Init(aPort, false, mPrincipal, aAddressReuse, 136 /* optional_argc = */ 1); 137 } else { 138 PRNetAddr prAddr; 139 PR_InitializeNetAddr(PR_IpAddrAny, aPort, &prAddr); 140 PRStatus status = PR_StringToNetAddr(aHost.BeginReading(), &prAddr); 141 if (status != PR_SUCCESS) { 142 return NS_ERROR_FAILURE; 143 } 144 145 mozilla::net::NetAddr addr(&prAddr); 146 rv = sock->InitWithAddress(&addr, mPrincipal, aAddressReuse, 147 /* optional_argc = */ 1); 148 } 149 150 if (NS_WARN_IF(NS_FAILED(rv))) { 151 return rv; 152 } 153 154 nsCOMPtr<nsINetAddr> laddr; 155 rv = sock->GetLocalAddr(getter_AddRefs(laddr)); 156 if (NS_WARN_IF(NS_FAILED(rv))) { 157 return rv; 158 } 159 uint16_t family; 160 rv = laddr->GetFamily(&family); 161 if (NS_WARN_IF(NS_FAILED(rv))) { 162 return rv; 163 } 164 if (family == nsINetAddr::FAMILY_INET) { 165 rv = sock->SetMulticastLoopback(aLoopback); 166 if (NS_WARN_IF(NS_FAILED(rv))) { 167 return rv; 168 } 169 } 170 // TODO: once bug 1252759 is fixed query buffer first and only increase 171 if (recvBufferSize != 0) { 172 rv = sock->SetRecvBufferSize(recvBufferSize); 173 if (NS_WARN_IF(NS_FAILED(rv))) { 174 UDPSOCKET_LOG( 175 ("%s: [this=%p] %s:%u failed to set recv buffer size to: %" PRIu32, 176 __FUNCTION__, this, nsCString(aHost).get(), aPort, recvBufferSize)); 177 } 178 } 179 if (sendBufferSize != 0) { 180 rv = sock->SetSendBufferSize(sendBufferSize); 181 if (NS_WARN_IF(NS_FAILED(rv))) { 182 UDPSOCKET_LOG( 183 ("%s: [this=%p] %s:%u failed to set send buffer size to: %" PRIu32, 184 __FUNCTION__, this, nsCString(aHost).get(), aPort, sendBufferSize)); 185 } 186 } 187 188 // register listener 189 rv = sock->AsyncListen(this); 190 if (NS_WARN_IF(NS_FAILED(rv))) { 191 return rv; 192 } 193 194 mSocket = sock; 195 196 return NS_OK; 197 } 198 199 static nsCOMPtr<nsIEventTarget> GetSTSThread() { 200 nsresult rv; 201 202 nsCOMPtr<nsIEventTarget> sts_thread; 203 204 sts_thread = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); 205 MOZ_ASSERT(NS_SUCCEEDED(rv)); 206 207 return sts_thread; 208 } 209 210 static void CheckSTSThread() { 211 DebugOnly<nsCOMPtr<nsIEventTarget>> sts_thread = GetSTSThread(); 212 213 ASSERT_ON_THREAD(sts_thread.value); 214 } 215 216 // Proxy the Connect() request to the STS thread, since it may block and 217 // should be done there. 218 mozilla::ipc::IPCResult UDPSocketParent::RecvConnect( 219 const UDPAddressInfo& aAddressInfo) { 220 nsCOMPtr<nsIEventTarget> target = GetCurrentSerialEventTarget(); 221 (void)NS_WARN_IF(NS_FAILED(GetSTSThread()->Dispatch( 222 WrapRunnable(RefPtr<UDPSocketParent>(this), &UDPSocketParent::DoConnect, 223 mSocket, target, aAddressInfo), 224 NS_DISPATCH_NORMAL))); 225 return IPC_OK(); 226 } 227 228 void UDPSocketParent::DoSendConnectResponse( 229 const UDPAddressInfo& aAddressInfo) { 230 // can't use directly with WrapRunnable due to warnings 231 (void)SendCallbackConnected(aAddressInfo); 232 } 233 234 void UDPSocketParent::SendConnectResponse( 235 const nsCOMPtr<nsIEventTarget>& aThread, 236 const UDPAddressInfo& aAddressInfo) { 237 (void)NS_WARN_IF(NS_FAILED(aThread->Dispatch( 238 WrapRunnable(RefPtr<UDPSocketParent>(this), 239 &UDPSocketParent::DoSendConnectResponse, aAddressInfo), 240 NS_DISPATCH_NORMAL))); 241 } 242 243 // Runs on STS thread 244 void UDPSocketParent::DoConnect(const nsCOMPtr<nsIUDPSocket>& aSocket, 245 const nsCOMPtr<nsIEventTarget>& aReturnThread, 246 const UDPAddressInfo& aAddressInfo) { 247 UDPSOCKET_LOG(("%s: %s:%u", __FUNCTION__, aAddressInfo.addr().get(), 248 aAddressInfo.port())); 249 if (NS_FAILED(ConnectInternal(aAddressInfo.addr(), aAddressInfo.port()))) { 250 SendInternalError(aReturnThread, __LINE__); 251 return; 252 } 253 CheckSTSThread(); 254 255 nsCOMPtr<nsINetAddr> localAddr; 256 aSocket->GetLocalAddr(getter_AddRefs(localAddr)); 257 258 nsCString addr; 259 if (NS_FAILED(localAddr->GetAddress(addr))) { 260 SendInternalError(aReturnThread, __LINE__); 261 return; 262 } 263 264 uint16_t port; 265 if (NS_FAILED(localAddr->GetPort(&port))) { 266 SendInternalError(aReturnThread, __LINE__); 267 return; 268 } 269 270 UDPSOCKET_LOG( 271 ("%s: SendConnectResponse: %s:%u", __FUNCTION__, addr.get(), port)); 272 SendConnectResponse(aReturnThread, UDPAddressInfo(addr, port)); 273 } 274 275 nsresult UDPSocketParent::ConnectInternal(const nsCString& aHost, 276 const uint16_t& aPort) { 277 nsresult rv; 278 279 UDPSOCKET_LOG(("%s: %s:%u", __FUNCTION__, nsCString(aHost).get(), aPort)); 280 281 if (!mSocket) { 282 return NS_ERROR_NOT_AVAILABLE; 283 } 284 285 PRNetAddr prAddr; 286 memset(&prAddr, 0, sizeof(prAddr)); 287 PR_InitializeNetAddr(PR_IpAddrAny, aPort, &prAddr); 288 PRStatus status = PR_StringToNetAddr(aHost.BeginReading(), &prAddr); 289 if (status != PR_SUCCESS) { 290 return NS_ERROR_FAILURE; 291 } 292 293 mozilla::net::NetAddr addr(&prAddr); 294 rv = mSocket->Connect(&addr); 295 if (NS_WARN_IF(NS_FAILED(rv))) { 296 return rv; 297 } 298 299 return NS_OK; 300 } 301 302 mozilla::ipc::IPCResult UDPSocketParent::RecvOutgoingData( 303 const UDPData& aData, const UDPSocketAddr& aAddr) { 304 if (!mSocket) { 305 NS_WARNING("sending socket is closed"); 306 FireInternalError(__LINE__); 307 return IPC_OK(); 308 } 309 310 nsresult rv; 311 if (mFilter) { 312 if (aAddr.type() != UDPSocketAddr::TNetAddr) { 313 return IPC_OK(); 314 } 315 316 // TODO, Packet filter doesn't support input stream yet. 317 if (aData.type() != UDPData::TArrayOfuint8_t) { 318 return IPC_OK(); 319 } 320 321 bool allowed; 322 const nsTArray<uint8_t>& data(aData.get_ArrayOfuint8_t()); 323 UDPSOCKET_LOG(("%s(%s:%d): Filtering outgoing packet", __FUNCTION__, 324 mAddress.addr().get(), mAddress.port())); 325 326 rv = mFilter->FilterPacket(&aAddr.get_NetAddr(), data.Elements(), 327 data.Length(), nsISocketFilter::SF_OUTGOING, 328 &allowed); 329 330 // Sending unallowed data, kill content. 331 if (NS_WARN_IF(NS_FAILED(rv)) || !allowed) { 332 return IPC_FAIL(this, "Content tried to send non STUN packet"); 333 } 334 } 335 336 switch (aData.type()) { 337 case UDPData::TArrayOfuint8_t: 338 Send(aData.get_ArrayOfuint8_t(), aAddr); 339 break; 340 case UDPData::TIPCStream: 341 Send(aData.get_IPCStream(), aAddr); 342 break; 343 default: 344 MOZ_ASSERT(false, "Invalid data type!"); 345 return IPC_OK(); 346 } 347 348 return IPC_OK(); 349 } 350 351 void UDPSocketParent::Send(const nsTArray<uint8_t>& aData, 352 const UDPSocketAddr& aAddr) { 353 nsresult rv; 354 uint32_t count; 355 switch (aAddr.type()) { 356 case UDPSocketAddr::TUDPAddressInfo: { 357 const UDPAddressInfo& addrInfo(aAddr.get_UDPAddressInfo()); 358 rv = mSocket->Send(addrInfo.addr(), addrInfo.port(), aData, &count); 359 break; 360 } 361 case UDPSocketAddr::TNetAddr: { 362 const NetAddr& addr(aAddr.get_NetAddr()); 363 rv = mSocket->SendWithAddress(&addr, aData.Elements(), aData.Length(), 364 &count); 365 break; 366 } 367 default: 368 MOZ_ASSERT(false, "Invalid address type!"); 369 return; 370 } 371 372 if (NS_WARN_IF(NS_FAILED(rv)) || count == 0) { 373 FireInternalError(__LINE__); 374 } 375 } 376 377 void UDPSocketParent::Send(const IPCStream& aStream, 378 const UDPSocketAddr& aAddr) { 379 nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(aStream); 380 381 if (NS_WARN_IF(!stream)) { 382 return; 383 } 384 385 nsresult rv; 386 switch (aAddr.type()) { 387 case UDPSocketAddr::TUDPAddressInfo: { 388 const UDPAddressInfo& addrInfo(aAddr.get_UDPAddressInfo()); 389 rv = mSocket->SendBinaryStream(addrInfo.addr(), addrInfo.port(), stream); 390 break; 391 } 392 case UDPSocketAddr::TNetAddr: { 393 const NetAddr& addr(aAddr.get_NetAddr()); 394 rv = mSocket->SendBinaryStreamWithAddress(&addr, stream); 395 break; 396 } 397 default: 398 MOZ_ASSERT(false, "Invalid address type!"); 399 return; 400 } 401 402 if (NS_FAILED(rv)) { 403 FireInternalError(__LINE__); 404 } 405 } 406 407 mozilla::ipc::IPCResult UDPSocketParent::RecvJoinMulticast( 408 const nsCString& aMulticastAddress, const nsCString& aInterface) { 409 if (!mSocket) { 410 NS_WARNING("multicast socket is closed"); 411 FireInternalError(__LINE__); 412 return IPC_OK(); 413 } 414 415 nsresult rv = mSocket->JoinMulticast(aMulticastAddress, aInterface); 416 417 if (NS_WARN_IF(NS_FAILED(rv))) { 418 FireInternalError(__LINE__); 419 } 420 421 return IPC_OK(); 422 } 423 424 mozilla::ipc::IPCResult UDPSocketParent::RecvLeaveMulticast( 425 const nsCString& aMulticastAddress, const nsCString& aInterface) { 426 if (!mSocket) { 427 NS_WARNING("multicast socket is closed"); 428 FireInternalError(__LINE__); 429 return IPC_OK(); 430 } 431 432 nsresult rv = mSocket->LeaveMulticast(aMulticastAddress, aInterface); 433 434 if (NS_WARN_IF(NS_FAILED(rv))) { 435 FireInternalError(__LINE__); 436 } 437 438 return IPC_OK(); 439 } 440 441 mozilla::ipc::IPCResult UDPSocketParent::RecvClose() { 442 if (!mSocket) { 443 return IPC_OK(); 444 } 445 446 nsresult rv = mSocket->Close(); 447 mSocket = nullptr; 448 449 (void)NS_WARN_IF(NS_FAILED(rv)); 450 451 return IPC_OK(); 452 } 453 454 mozilla::ipc::IPCResult UDPSocketParent::RecvRequestDelete() { 455 (void)Send__delete__(this); 456 return IPC_OK(); 457 } 458 459 void UDPSocketParent::ActorDestroy(ActorDestroyReason why) { 460 MOZ_ASSERT(mIPCOpen); 461 mIPCOpen = false; 462 if (mSocket) { 463 mSocket->Close(); 464 } 465 mSocket = nullptr; 466 } 467 468 // nsIUDPSocketListener 469 470 NS_IMETHODIMP 471 UDPSocketParent::OnPacketReceived(nsIUDPSocket* aSocket, 472 nsIUDPMessage* aMessage) { 473 // receiving packet from remote host, forward the message content to child 474 // process 475 if (!mIPCOpen) { 476 return NS_OK; 477 } 478 479 uint16_t port; 480 nsCString ip; 481 nsCOMPtr<nsINetAddr> fromAddr; 482 aMessage->GetFromAddr(getter_AddRefs(fromAddr)); 483 fromAddr->GetPort(&port); 484 fromAddr->GetAddress(ip); 485 486 nsCString data; 487 aMessage->GetData(data); 488 489 const char* buffer = data.get(); 490 uint32_t len = data.Length(); 491 UDPSOCKET_LOG(("%s: %s:%u, length %u", __FUNCTION__, ip.get(), port, len)); 492 493 if (mFilter) { 494 bool allowed; 495 mozilla::net::NetAddr addr; 496 fromAddr->GetNetAddr(&addr); 497 UDPSOCKET_LOG(("%s(%s:%d): Filtering incoming packet", __FUNCTION__, 498 mAddress.addr().get(), mAddress.port())); 499 nsresult rv = mFilter->FilterPacket(&addr, (const uint8_t*)buffer, len, 500 nsISocketFilter::SF_INCOMING, &allowed); 501 // Receiving unallowed data, drop. 502 if (NS_WARN_IF(NS_FAILED(rv)) || !allowed) { 503 if (!allowed) { 504 UDPSOCKET_LOG(("%s: not allowed", __FUNCTION__)); 505 } 506 return NS_OK; 507 } 508 } 509 510 FallibleTArray<uint8_t> fallibleArray; 511 if (!fallibleArray.InsertElementsAt(0, buffer, len, fallible)) { 512 FireInternalError(__LINE__); 513 return NS_ERROR_OUT_OF_MEMORY; 514 } 515 nsTArray<uint8_t> infallibleArray{std::move(fallibleArray)}; 516 517 // compose callback 518 (void)SendCallbackReceivedData(UDPAddressInfo(ip, port), infallibleArray); 519 520 return NS_OK; 521 } 522 523 NS_IMETHODIMP 524 UDPSocketParent::OnStopListening(nsIUDPSocket* aSocket, nsresult aStatus) { 525 // underlying socket is dead, send state update to child process 526 if (mIPCOpen) { 527 (void)SendCallbackClosed(); 528 } 529 return NS_OK; 530 } 531 532 void UDPSocketParent::FireInternalError(uint32_t aLineNo) { 533 if (!mIPCOpen) { 534 return; 535 } 536 537 (void)SendCallbackError("Internal error"_ns, nsLiteralCString(__FILE__), 538 aLineNo); 539 } 540 541 void UDPSocketParent::SendInternalError(const nsCOMPtr<nsIEventTarget>& aThread, 542 uint32_t aLineNo) { 543 UDPSOCKET_LOG(("SendInternalError: %u", aLineNo)); 544 (void)NS_WARN_IF(NS_FAILED(aThread->Dispatch( 545 WrapRunnable(RefPtr<UDPSocketParent>(this), 546 &UDPSocketParent::FireInternalError, aLineNo), 547 NS_DISPATCH_NORMAL))); 548 } 549 550 } // namespace dom 551 } // namespace mozilla