123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611 |
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
- #include "nsIServiceManager.h"
- #include "UDPSocketParent.h"
- #include "UDPSocket.h"
- #include "nsComponentManagerUtils.h"
- #include "nsIUDPSocket.h"
- #include "nsINetAddr.h"
- #include "nsNetCID.h"
- #include "mozilla/AppProcessChecker.h"
- #include "mozilla/Unused.h"
- #include "mozilla/ipc/InputStreamUtils.h"
- #include "mozilla/net/DNS.h"
- #include "mozilla/net/NeckoCommon.h"
- #include "mozilla/net/PNeckoParent.h"
- #include "nsIPermissionManager.h"
- #include "nsIScriptSecurityManager.h"
- #include "mozilla/dom/ContentParent.h"
- #include "mozilla/ipc/PBackgroundParent.h"
- #include "mtransport/runnable_utils.h"
- using namespace mozilla::net;
- namespace mozilla {
- namespace dom {
- NS_IMPL_ISUPPORTS(UDPSocketParent, nsIUDPSocketListener)
- UDPSocketParent::UDPSocketParent(PBackgroundParent* aManager)
- : mBackgroundManager(aManager)
- , mNeckoManager(nullptr)
- , mIPCOpen(true)
- {
- }
- UDPSocketParent::UDPSocketParent(PNeckoParent* aManager)
- : mBackgroundManager(nullptr)
- , mNeckoManager(aManager)
- , mIPCOpen(true)
- {
- }
- UDPSocketParent::~UDPSocketParent()
- {
- }
- bool
- UDPSocketParent::Init(const IPC::Principal& aPrincipal,
- const nsACString& aFilter)
- {
- MOZ_ASSERT_IF(mBackgroundManager, !aPrincipal);
- // will be used once we move all UDPSocket to PBackground, or
- // if we add in Principal checking for mtransport
- Unused << mBackgroundManager;
- mPrincipal = aPrincipal;
- if (net::UsingNeckoIPCSecurity() &&
- mPrincipal &&
- !ContentParent::IgnoreIPCPrincipal()) {
- if (mNeckoManager) {
- if (!AssertAppPrincipal(mNeckoManager->Manager(), mPrincipal)) {
- return false;
- }
- } else {
- // PBackground is (for now) using a STUN filter for verification
- // it's not being used for DoS
- }
- nsCOMPtr<nsIPermissionManager> permMgr =
- services::GetPermissionManager();
- if (!permMgr) {
- NS_WARNING("No PermissionManager available!");
- return false;
- }
- uint32_t permission = nsIPermissionManager::DENY_ACTION;
- permMgr->TestExactPermissionFromPrincipal(mPrincipal, "udp-socket",
- &permission);
- if (permission != nsIPermissionManager::ALLOW_ACTION) {
- return false;
- }
- }
- if (!aFilter.IsEmpty()) {
- nsAutoCString contractId(NS_NETWORK_UDP_SOCKET_FILTER_HANDLER_PREFIX);
- contractId.Append(aFilter);
- nsCOMPtr<nsISocketFilterHandler> filterHandler =
- do_GetService(contractId.get());
- if (filterHandler) {
- nsresult rv = filterHandler->NewFilter(getter_AddRefs(mFilter));
- if (NS_FAILED(rv)) {
- printf_stderr("Cannot create filter that content specified. "
- "filter name: %s, error code: %u.", aFilter.BeginReading(), static_cast<uint32_t>(rv));
- return false;
- }
- } else {
- printf_stderr("Content doesn't have a valid filter. "
- "filter name: %s.", aFilter.BeginReading());
- return false;
- }
- }
- // We don't have browser actors in xpcshell, and hence can't run automated
- // tests without this loophole.
- if (net::UsingNeckoIPCSecurity() && !mFilter &&
- (!mPrincipal || ContentParent::IgnoreIPCPrincipal())) {
- return false;
- }
- return true;
- }
- // PUDPSocketParent methods
- bool
- UDPSocketParent::RecvBind(const UDPAddressInfo& aAddressInfo,
- const bool& aAddressReuse, const bool& aLoopback,
- const uint32_t& recvBufferSize,
- const uint32_t& sendBufferSize)
- {
- UDPSOCKET_LOG(("%s: %s:%u", __FUNCTION__, aAddressInfo.addr().get(), aAddressInfo.port()));
- if (NS_FAILED(BindInternal(aAddressInfo.addr(), aAddressInfo.port(),
- aAddressReuse, aLoopback, recvBufferSize,
- sendBufferSize))) {
- FireInternalError(__LINE__);
- return true;
- }
- nsCOMPtr<nsINetAddr> localAddr;
- mSocket->GetLocalAddr(getter_AddRefs(localAddr));
- nsCString addr;
- if (NS_FAILED(localAddr->GetAddress(addr))) {
- FireInternalError(__LINE__);
- return true;
- }
- uint16_t port;
- if (NS_FAILED(localAddr->GetPort(&port))) {
- FireInternalError(__LINE__);
- return true;
- }
- UDPSOCKET_LOG(("%s: SendCallbackOpened: %s:%u", __FUNCTION__, addr.get(), port));
- mozilla::Unused << SendCallbackOpened(UDPAddressInfo(addr, port));
- return true;
- }
- nsresult
- UDPSocketParent::BindInternal(const nsCString& aHost, const uint16_t& aPort,
- const bool& aAddressReuse, const bool& aLoopback,
- const uint32_t& recvBufferSize,
- const uint32_t& sendBufferSize)
- {
- nsresult rv;
- UDPSOCKET_LOG(("%s: [this=%p] %s:%u addressReuse: %d loopback: %d recvBufferSize: %lu, sendBufferSize: %lu",
- __FUNCTION__, this, nsCString(aHost).get(), aPort,
- aAddressReuse, aLoopback, recvBufferSize, sendBufferSize));
- nsCOMPtr<nsIUDPSocket> sock =
- do_CreateInstance("@mozilla.org/network/udp-socket;1", &rv);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (aHost.IsEmpty()) {
- rv = sock->Init(aPort, false, mPrincipal, aAddressReuse,
- /* optional_argc = */ 1);
- } else {
- PRNetAddr prAddr;
- PR_InitializeNetAddr(PR_IpAddrAny, aPort, &prAddr);
- PRStatus status = PR_StringToNetAddr(aHost.BeginReading(), &prAddr);
- if (status != PR_SUCCESS) {
- return NS_ERROR_FAILURE;
- }
- mozilla::net::NetAddr addr;
- PRNetAddrToNetAddr(&prAddr, &addr);
- rv = sock->InitWithAddress(&addr, mPrincipal, aAddressReuse,
- /* optional_argc = */ 1);
- }
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- nsCOMPtr<nsINetAddr> laddr;
- rv = sock->GetLocalAddr(getter_AddRefs(laddr));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- uint16_t family;
- rv = laddr->GetFamily(&family);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (family == nsINetAddr::FAMILY_INET) {
- rv = sock->SetMulticastLoopback(aLoopback);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- }
- // TODO: once bug 1252759 is fixed query buffer first and only increase
- if (recvBufferSize != 0) {
- rv = sock->SetRecvBufferSize(recvBufferSize);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- UDPSOCKET_LOG(("%s: [this=%p] %s:%u failed to set recv buffer size to: %lu", __FUNCTION__, this, nsCString(aHost).get(), aPort, recvBufferSize));
- }
- }
- if (sendBufferSize != 0) {
- rv = sock->SetSendBufferSize(sendBufferSize);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- UDPSOCKET_LOG(("%s: [this=%p] %s:%u failed to set send buffer size to: %lu", __FUNCTION__, this, nsCString(aHost).get(), aPort, sendBufferSize));
- }
- }
- // register listener
- rv = sock->AsyncListen(this);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- mSocket = sock;
- return NS_OK;
- }
- static nsCOMPtr<nsIEventTarget> GetSTSThread()
- {
- nsresult rv;
- nsCOMPtr<nsIEventTarget> sts_thread;
- sts_thread = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
- MOZ_ASSERT(NS_SUCCEEDED(rv));
- return sts_thread;
- }
- static void CheckSTSThread()
- {
- DebugOnly<nsCOMPtr<nsIEventTarget>> sts_thread = GetSTSThread();
- ASSERT_ON_THREAD(sts_thread.value);
- }
- // Proxy the Connect() request to the STS thread, since it may block and
- // should be done there.
- bool
- UDPSocketParent::RecvConnect(const UDPAddressInfo& aAddressInfo)
- {
- nsCOMPtr<nsIEventTarget> thread(NS_GetCurrentThread());
- Unused <<
- NS_WARN_IF(NS_FAILED(GetSTSThread()->Dispatch(WrapRunnable(
- RefPtr<UDPSocketParent>(this),
- &UDPSocketParent::DoConnect,
- mSocket,
- thread,
- aAddressInfo),
- NS_DISPATCH_NORMAL)));
- return true;
- }
- void
- UDPSocketParent::DoSendConnectResponse(const UDPAddressInfo& aAddressInfo)
- {
- // can't use directly with WrapRunnable due to warnings
- mozilla::Unused << SendCallbackConnected(aAddressInfo);
- }
- void
- UDPSocketParent::SendConnectResponse(nsIEventTarget *aThread,
- const UDPAddressInfo& aAddressInfo)
- {
- Unused <<
- NS_WARN_IF(NS_FAILED(aThread->Dispatch(WrapRunnable(
- RefPtr<UDPSocketParent>(this),
- &UDPSocketParent::DoSendConnectResponse,
- aAddressInfo),
- NS_DISPATCH_NORMAL)));
- }
- // Runs on STS thread
- void
- UDPSocketParent::DoConnect(nsCOMPtr<nsIUDPSocket>& aSocket,
- nsCOMPtr<nsIEventTarget>& aReturnThread,
- const UDPAddressInfo& aAddressInfo)
- {
- UDPSOCKET_LOG(("%s: %s:%u", __FUNCTION__, aAddressInfo.addr().get(), aAddressInfo.port()));
- if (NS_FAILED(ConnectInternal(aAddressInfo.addr(), aAddressInfo.port()))) {
- SendInternalError(aReturnThread, __LINE__);
- return;
- }
- CheckSTSThread();
- nsCOMPtr<nsINetAddr> localAddr;
- aSocket->GetLocalAddr(getter_AddRefs(localAddr));
- nsCString addr;
- if (NS_FAILED(localAddr->GetAddress(addr))) {
- SendInternalError(aReturnThread, __LINE__);
- return;
- }
- uint16_t port;
- if (NS_FAILED(localAddr->GetPort(&port))) {
- SendInternalError(aReturnThread, __LINE__);
- return;
- }
- UDPSOCKET_LOG(("%s: SendConnectResponse: %s:%u", __FUNCTION__, addr.get(), port));
- SendConnectResponse(aReturnThread, UDPAddressInfo(addr, port));
- }
- nsresult
- UDPSocketParent::ConnectInternal(const nsCString& aHost, const uint16_t& aPort)
- {
- nsresult rv;
- UDPSOCKET_LOG(("%s: %s:%u", __FUNCTION__, nsCString(aHost).get(), aPort));
- if (!mSocket) {
- return NS_ERROR_NOT_AVAILABLE;
- }
- PRNetAddr prAddr;
- PR_InitializeNetAddr(PR_IpAddrAny, aPort, &prAddr);
- PRStatus status = PR_StringToNetAddr(aHost.BeginReading(), &prAddr);
- if (status != PR_SUCCESS) {
- return NS_ERROR_FAILURE;
- }
- mozilla::net::NetAddr addr;
- PRNetAddrToNetAddr(&prAddr, &addr);
- rv = mSocket->Connect(&addr);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- return NS_OK;
- }
- bool
- UDPSocketParent::RecvOutgoingData(const UDPData& aData,
- const UDPSocketAddr& aAddr)
- {
- if (!mSocket) {
- NS_WARNING("sending socket is closed");
- FireInternalError(__LINE__);
- return true;
- }
- nsresult rv;
- if (mFilter) {
- if (aAddr.type() != UDPSocketAddr::TNetAddr) {
- return true;
- }
- // TODO, Packet filter doesn't support input stream yet.
- if (aData.type() != UDPData::TArrayOfuint8_t) {
- return true;
- }
- bool allowed;
- const InfallibleTArray<uint8_t>& data(aData.get_ArrayOfuint8_t());
- rv = mFilter->FilterPacket(&aAddr.get_NetAddr(), data.Elements(),
- data.Length(), nsISocketFilter::SF_OUTGOING,
- &allowed);
- // Sending unallowed data, kill content.
- if (NS_WARN_IF(NS_FAILED(rv)) || !allowed) {
- return false;
- }
- }
- switch(aData.type()) {
- case UDPData::TArrayOfuint8_t:
- Send(aData.get_ArrayOfuint8_t(), aAddr);
- break;
- case UDPData::TInputStreamParams:
- Send(aData.get_InputStreamParams(), aAddr);
- break;
- default:
- MOZ_ASSERT(false, "Invalid data type!");
- return true;
- }
- return true;
- }
- void
- UDPSocketParent::Send(const InfallibleTArray<uint8_t>& aData,
- const UDPSocketAddr& aAddr)
- {
- nsresult rv;
- uint32_t count;
- switch(aAddr.type()) {
- case UDPSocketAddr::TUDPAddressInfo: {
- const UDPAddressInfo& addrInfo(aAddr.get_UDPAddressInfo());
- rv = mSocket->Send(addrInfo.addr(), addrInfo.port(),
- aData.Elements(), aData.Length(), &count);
- break;
- }
- case UDPSocketAddr::TNetAddr: {
- const NetAddr& addr(aAddr.get_NetAddr());
- rv = mSocket->SendWithAddress(&addr, aData.Elements(),
- aData.Length(), &count);
- break;
- }
- default:
- MOZ_ASSERT(false, "Invalid address type!");
- return;
- }
- if (NS_WARN_IF(NS_FAILED(rv)) || count == 0) {
- FireInternalError(__LINE__);
- }
- }
- void
- UDPSocketParent::Send(const InputStreamParams& aStream,
- const UDPSocketAddr& aAddr)
- {
- nsTArray<mozilla::ipc::FileDescriptor> fds;
- nsCOMPtr<nsIInputStream> stream = DeserializeInputStream(aStream, fds);
- if (NS_WARN_IF(!stream)) {
- return;
- }
- nsresult rv;
- switch(aAddr.type()) {
- case UDPSocketAddr::TUDPAddressInfo: {
- const UDPAddressInfo& addrInfo(aAddr.get_UDPAddressInfo());
- rv = mSocket->SendBinaryStream(addrInfo.addr(), addrInfo.port(), stream);
- break;
- }
- case UDPSocketAddr::TNetAddr: {
- const NetAddr& addr(aAddr.get_NetAddr());
- rv = mSocket->SendBinaryStreamWithAddress(&addr, stream);
- break;
- }
- default:
- MOZ_ASSERT(false, "Invalid address type!");
- return;
- }
- if (NS_FAILED(rv)) {
- FireInternalError(__LINE__);
- }
- }
- bool
- UDPSocketParent::RecvJoinMulticast(const nsCString& aMulticastAddress,
- const nsCString& aInterface)
- {
- nsresult rv = mSocket->JoinMulticast(aMulticastAddress, aInterface);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- FireInternalError(__LINE__);
- }
- return true;
- }
- bool
- UDPSocketParent::RecvLeaveMulticast(const nsCString& aMulticastAddress,
- const nsCString& aInterface)
- {
- nsresult rv = mSocket->LeaveMulticast(aMulticastAddress, aInterface);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- FireInternalError(__LINE__);
- }
- return true;
- }
- bool
- UDPSocketParent::RecvClose()
- {
- if (!mSocket) {
- return true;
- }
- nsresult rv = mSocket->Close();
- mSocket = nullptr;
- mozilla::Unused << NS_WARN_IF(NS_FAILED(rv));
- return true;
- }
- bool
- UDPSocketParent::RecvRequestDelete()
- {
- mozilla::Unused << Send__delete__(this);
- return true;
- }
- void
- UDPSocketParent::ActorDestroy(ActorDestroyReason why)
- {
- MOZ_ASSERT(mIPCOpen);
- mIPCOpen = false;
- if (mSocket) {
- mSocket->Close();
- }
- mSocket = nullptr;
- }
- // nsIUDPSocketListener
- NS_IMETHODIMP
- UDPSocketParent::OnPacketReceived(nsIUDPSocket* aSocket, nsIUDPMessage* aMessage)
- {
- // receiving packet from remote host, forward the message content to child process
- if (!mIPCOpen) {
- return NS_OK;
- }
- uint16_t port;
- nsCString ip;
- nsCOMPtr<nsINetAddr> fromAddr;
- aMessage->GetFromAddr(getter_AddRefs(fromAddr));
- fromAddr->GetPort(&port);
- fromAddr->GetAddress(ip);
- nsCString data;
- aMessage->GetData(data);
- const char* buffer = data.get();
- uint32_t len = data.Length();
- UDPSOCKET_LOG(("%s: %s:%u, length %u", __FUNCTION__, ip.get(), port, len));
- if (mFilter) {
- bool allowed;
- mozilla::net::NetAddr addr;
- fromAddr->GetNetAddr(&addr);
- nsresult rv = mFilter->FilterPacket(&addr,
- (const uint8_t*)buffer, len,
- nsISocketFilter::SF_INCOMING,
- &allowed);
- // Receiving unallowed data, drop.
- if (NS_WARN_IF(NS_FAILED(rv)) || !allowed) {
- if (!allowed) {
- UDPSOCKET_LOG(("%s: not allowed", __FUNCTION__));
- }
- return NS_OK;
- }
- }
- FallibleTArray<uint8_t> fallibleArray;
- if (!fallibleArray.InsertElementsAt(0, buffer, len, fallible)) {
- FireInternalError(__LINE__);
- return NS_ERROR_OUT_OF_MEMORY;
- }
- InfallibleTArray<uint8_t> infallibleArray;
- infallibleArray.SwapElements(fallibleArray);
- // compose callback
- mozilla::Unused << SendCallbackReceivedData(UDPAddressInfo(ip, port), infallibleArray);
- return NS_OK;
- }
- NS_IMETHODIMP
- UDPSocketParent::OnStopListening(nsIUDPSocket* aSocket, nsresult aStatus)
- {
- // underlying socket is dead, send state update to child process
- if (mIPCOpen) {
- mozilla::Unused << SendCallbackClosed();
- }
- return NS_OK;
- }
- void
- UDPSocketParent::FireInternalError(uint32_t aLineNo)
- {
- if (!mIPCOpen) {
- return;
- }
- mozilla::Unused << SendCallbackError(NS_LITERAL_CSTRING("Internal error"),
- NS_LITERAL_CSTRING(__FILE__), aLineNo);
- }
- void
- UDPSocketParent::SendInternalError(nsIEventTarget *aThread,
- uint32_t aLineNo)
- {
- UDPSOCKET_LOG(("SendInternalError: %u", aLineNo));
- Unused <<
- NS_WARN_IF(NS_FAILED(aThread->Dispatch(WrapRunnable(
- RefPtr<UDPSocketParent>(this),
- &UDPSocketParent::FireInternalError,
- aLineNo),
- NS_DISPATCH_NORMAL)));
- }
- } // namespace dom
- } // namespace mozilla
|