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