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