michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=8 sts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsIServiceManager.h" michael@0: #include "UDPSocketParent.h" michael@0: #include "nsComponentManagerUtils.h" michael@0: #include "nsIUDPSocket.h" michael@0: #include "nsINetAddr.h" michael@0: #include "mozilla/unused.h" michael@0: #include "mozilla/net/DNS.h" michael@0: michael@0: namespace mozilla { michael@0: namespace dom { michael@0: michael@0: static void michael@0: FireInternalError(mozilla::net::PUDPSocketParent *aActor, uint32_t aLineNo) michael@0: { michael@0: mozilla::unused << michael@0: aActor->SendCallback(NS_LITERAL_CSTRING("onerror"), michael@0: UDPError(NS_LITERAL_CSTRING("Internal error"), michael@0: NS_LITERAL_CSTRING(__FILE__), aLineNo, 0), michael@0: NS_LITERAL_CSTRING("connecting")); michael@0: } michael@0: michael@0: static nsresult michael@0: ConvertNetAddrToString(mozilla::net::NetAddr &netAddr, nsACString *address, uint16_t *port) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(address); michael@0: NS_ENSURE_ARG_POINTER(port); michael@0: michael@0: *port = 0; michael@0: uint32_t bufSize = 0; michael@0: michael@0: switch(netAddr.raw.family) { michael@0: case AF_INET: michael@0: *port = PR_ntohs(netAddr.inet.port); michael@0: bufSize = mozilla::net::kIPv4CStrBufSize; michael@0: break; michael@0: case AF_INET6: michael@0: *port = PR_ntohs(netAddr.inet6.port); michael@0: bufSize = mozilla::net::kIPv6CStrBufSize; michael@0: break; michael@0: default: michael@0: //impossible michael@0: MOZ_ASSERT(false, "Unexpected address family"); michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: address->SetCapacity(bufSize); michael@0: NetAddrToString(&netAddr, address->BeginWriting(), bufSize); michael@0: address->SetLength(strlen(address->BeginReading())); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(UDPSocketParent, nsIUDPSocketListener) michael@0: michael@0: UDPSocketParent::~UDPSocketParent() michael@0: { michael@0: } michael@0: michael@0: // PUDPSocketParent methods michael@0: michael@0: bool michael@0: UDPSocketParent::Init(const nsCString &aHost, const uint16_t aPort) michael@0: { michael@0: nsresult rv; michael@0: NS_ASSERTION(mFilter, "No packet filter"); michael@0: michael@0: nsCOMPtr sock = michael@0: do_CreateInstance("@mozilla.org/network/udp-socket;1", &rv); michael@0: if (NS_FAILED(rv)) { michael@0: FireInternalError(this, __LINE__); michael@0: return true; michael@0: } michael@0: michael@0: if (aHost.IsEmpty()) { michael@0: rv = sock->Init(aPort, false); michael@0: } else { michael@0: PRNetAddr prAddr; michael@0: PR_InitializeNetAddr(PR_IpAddrAny, aPort, &prAddr); michael@0: PRStatus status = PR_StringToNetAddr(aHost.BeginReading(), &prAddr); michael@0: if (status != PR_SUCCESS) { michael@0: FireInternalError(this, __LINE__); michael@0: return true; michael@0: } michael@0: michael@0: mozilla::net::NetAddr addr; michael@0: PRNetAddrToNetAddr(&prAddr, &addr); michael@0: rv = sock->InitWithAddress(&addr); michael@0: } michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: FireInternalError(this, __LINE__); michael@0: return true; michael@0: } michael@0: michael@0: mSocket = sock; michael@0: michael@0: net::NetAddr localAddr; michael@0: mSocket->GetAddress(&localAddr); michael@0: michael@0: uint16_t port; michael@0: nsCString addr; michael@0: rv = ConvertNetAddrToString(localAddr, &addr, &port); michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: FireInternalError(this, __LINE__); michael@0: return true; michael@0: } michael@0: michael@0: // register listener michael@0: mSocket->AsyncListen(this); michael@0: mozilla::unused << michael@0: PUDPSocketParent::SendCallback(NS_LITERAL_CSTRING("onopen"), michael@0: UDPAddressInfo(addr, port), michael@0: NS_LITERAL_CSTRING("connected")); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: UDPSocketParent::RecvData(const InfallibleTArray &aData, michael@0: const nsCString& aRemoteAddress, michael@0: const uint16_t& aPort) michael@0: { michael@0: NS_ENSURE_TRUE(mSocket, true); michael@0: NS_ASSERTION(mFilter, "No packet filter"); michael@0: // TODO, Bug 933102, filter packets that are sent with hostname. michael@0: // Until then we simply throw away packets that are sent to a hostname. michael@0: return true; michael@0: michael@0: #if 0 michael@0: // Enable this once we have filtering working with hostname delivery. michael@0: uint32_t count; michael@0: nsresult rv = mSocket->Send(aRemoteAddress, michael@0: aPort, aData.Elements(), michael@0: aData.Length(), &count); michael@0: mozilla::unused << michael@0: PUDPSocketParent::SendCallback(NS_LITERAL_CSTRING("onsent"), michael@0: UDPSendResult(rv), michael@0: NS_LITERAL_CSTRING("connected")); michael@0: NS_ENSURE_SUCCESS(rv, true); michael@0: NS_ENSURE_TRUE(count > 0, true); michael@0: return true; michael@0: #endif michael@0: } michael@0: michael@0: bool michael@0: UDPSocketParent::RecvDataWithAddress(const InfallibleTArray& aData, michael@0: const mozilla::net::NetAddr& aAddr) michael@0: { michael@0: NS_ENSURE_TRUE(mSocket, true); michael@0: NS_ASSERTION(mFilter, "No packet filter"); michael@0: michael@0: uint32_t count; michael@0: nsresult rv; michael@0: bool allowed; michael@0: rv = mFilter->FilterPacket(&aAddr, aData.Elements(), michael@0: aData.Length(), nsIUDPSocketFilter::SF_OUTGOING, michael@0: &allowed); michael@0: // Sending unallowed data, kill content. michael@0: NS_ENSURE_SUCCESS(rv, false); michael@0: NS_ENSURE_TRUE(allowed, false); michael@0: michael@0: rv = mSocket->SendWithAddress(&aAddr, aData.Elements(), michael@0: aData.Length(), &count); michael@0: mozilla::unused << michael@0: PUDPSocketParent::SendCallback(NS_LITERAL_CSTRING("onsent"), michael@0: UDPSendResult(rv), michael@0: NS_LITERAL_CSTRING("connected")); michael@0: NS_ENSURE_SUCCESS(rv, true); michael@0: NS_ENSURE_TRUE(count > 0, true); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: UDPSocketParent::RecvClose() michael@0: { michael@0: NS_ENSURE_TRUE(mSocket, true); michael@0: nsresult rv = mSocket->Close(); michael@0: mSocket = nullptr; michael@0: NS_ENSURE_SUCCESS(rv, true); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: UDPSocketParent::RecvRequestDelete() michael@0: { michael@0: mozilla::unused << Send__delete__(this); michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: UDPSocketParent::ActorDestroy(ActorDestroyReason why) michael@0: { michael@0: MOZ_ASSERT(mIPCOpen); michael@0: mIPCOpen = false; michael@0: if (mSocket) { michael@0: mSocket->Close(); michael@0: } michael@0: mSocket = nullptr; michael@0: } michael@0: michael@0: // nsIUDPSocketListener michael@0: michael@0: NS_IMETHODIMP michael@0: UDPSocketParent::OnPacketReceived(nsIUDPSocket* aSocket, nsIUDPMessage* aMessage) michael@0: { michael@0: // receiving packet from remote host, forward the message content to child process michael@0: if (!mIPCOpen) { michael@0: return NS_OK; michael@0: } michael@0: NS_ASSERTION(mFilter, "No packet filter"); michael@0: michael@0: uint16_t port; michael@0: nsCString ip; michael@0: nsCOMPtr fromAddr; michael@0: aMessage->GetFromAddr(getter_AddRefs(fromAddr)); michael@0: fromAddr->GetPort(&port); michael@0: fromAddr->GetAddress(ip); michael@0: michael@0: nsCString data; michael@0: aMessage->GetData(data); michael@0: michael@0: const char* buffer = data.get(); michael@0: uint32_t len = data.Length(); michael@0: michael@0: bool allowed; michael@0: mozilla::net::NetAddr addr; michael@0: fromAddr->GetNetAddr(&addr); michael@0: nsresult rv = mFilter->FilterPacket(&addr, michael@0: (const uint8_t*)buffer, len, michael@0: nsIUDPSocketFilter::SF_INCOMING, michael@0: &allowed); michael@0: // Receiving unallowed data, drop. michael@0: NS_ENSURE_SUCCESS(rv, NS_OK); michael@0: NS_ENSURE_TRUE(allowed, NS_OK); michael@0: michael@0: FallibleTArray fallibleArray; michael@0: if (!fallibleArray.InsertElementsAt(0, buffer, len)) { michael@0: FireInternalError(this, __LINE__); michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: InfallibleTArray infallibleArray; michael@0: infallibleArray.SwapElements(fallibleArray); michael@0: michael@0: // compose callback michael@0: mozilla::unused << michael@0: PUDPSocketParent::SendCallback(NS_LITERAL_CSTRING("ondata"), michael@0: UDPMessage(ip, port, infallibleArray), michael@0: NS_LITERAL_CSTRING("connected")); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: UDPSocketParent::OnStopListening(nsIUDPSocket* aSocket, nsresult aStatus) michael@0: { michael@0: // underlying socket is dead, send state update to child process michael@0: if (mIPCOpen) { michael@0: mozilla::unused << michael@0: PUDPSocketParent::SendCallback(NS_LITERAL_CSTRING("onclose"), michael@0: mozilla::void_t(), michael@0: NS_LITERAL_CSTRING("closed")); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: } // namespace dom michael@0: } // namespace mozilla